diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d4c8ab06d12844e1c0557109566027cb465c1bf4
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..b46e4786837463e2e0c8a4e09593b6e954dde127
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/edit.diff
@@ -0,0 +1,235 @@
+--- a/original.py
++++ b/original.py
+@@ -1,216 +1,222 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+ import math
+ import random
+
+ def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+- 'num_starts': 12, # Number of independent SA runs
+- 't_start': 0.05, # Initial temperature
++ 'num_starts': 16, # More starts to explore different basins
++ 't_start': 0.06, # Higher initial temperature for more exploration
+ 't_end': 1e-6, # Final temperature
+- 'cooling_rate': 0.998, # Geometric cooling factor
+- 'moves_per_temp': 60, # Number of moves at each temperature step
+- 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
++ 'cooling_rate': 0.999, # Slower cooling for more thorough search
++ 'moves_per_temp': 50, # Adjusted moves to balance longer schedule
++ 'max_step_size': 0.20, # Larger initial step size
+ 'radius_iter': 250, # Radius calculation iterations during search
+- 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+- 'cluster_size': 4,
++ 'move_weights': [0.70, 0.20, 0.10], # Increased probability for powerful swap move
++ 'cluster_size': 5, # Slightly larger clusters
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+- if i < config['num_starts'] // 2:
++ # Prioritize structured starts (12 of 16), which are generally more promising
++ if i < 12:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+- # Place the 26th circle in a strategic corner or center
+- extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
++ # Use a richer set of candidate positions for the 26th circle
++ extra_pos_candidates = [
++ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
++ [0.5, 0.5], # Center
++ [0.2, 0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8], # Grid interstitial (spacing=0.2)
++ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
++ ]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fa5a9ebe7d113d7114155bce9bb2ac44a405dc4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/main.py
@@ -0,0 +1,222 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 16, # More starts to explore different basins
+ 't_start': 0.06, # Higher initial temperature for more exploration
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.999, # Slower cooling for more thorough search
+ 'moves_per_temp': 50, # Adjusted moves to balance longer schedule
+ 'max_step_size': 0.20, # Larger initial step size
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.70, 0.20, 0.10], # Increased probability for powerful swap move
+ 'cluster_size': 5, # Slightly larger clusters
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ # Prioritize structured starts (12 of 16), which are generally more promising
+ if i < 12:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Use a richer set of candidate positions for the 26th circle
+ extra_pos_candidates = [
+ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
+ [0.5, 0.5], # Center
+ [0.2, 0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8], # Grid interstitial (spacing=0.2)
+ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
+ ]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ff95302fa28431aa45cfdecfc5bf767e17899a4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/original.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 12, # Number of independent SA runs
+ 't_start': 0.05, # Initial temperature
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.998, # Geometric cooling factor
+ 'moves_per_temp': 60, # Number of moves at each temperature step
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+ 'cluster_size': 4,
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Place the 26th circle in a strategic corner or center
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..b53c8bbf1e06cc8e001bcaf4a85371cacd301a39
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results
+Run 1/1 completed in 32125.27 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.5604436893300666
+ public: {'centers_str': ' centers[0] = (0.4791, 0.5712)\n centers[1] = (0.2653, 0.7270)\n centers[2] = (0.6797, 0.7584)\n centers[3] = (0.6748, 0.3125)\n centers[4] = (0.4032, 0.2676)\n centers[5] = (0.2807, 0.5100)\n centers[6] = (0.8859, 0.4508)\n centers[7] = (0.0798, 0.0798)\n centers[8] = (0.6921, 0.5575)\n centers[9] = (0.5696, 0.0994)\n centers[10] = (0.9005, 0.2376)\n centers[11] = (0.9297, 0.0703)\n centers[12] = (0.5538, 0.9026)\n centers[13] = (0.3512, 0.8946)\n centers[14] = (0.5075, 0.4749)\n centers[15] = (0.7652, 0.0962)\n centers[16] = (0.0985, 0.6553)\n centers[17] = (0.4703, 0.6972)\n centers[18] = (0.7273, 0.9228)\n centers[19] = (0.2524, 0.0933)\n centers[20] = (0.0739, 0.4847)\n centers[21] = (0.1233, 0.8767)\n centers[22] = (0.8802, 0.6845)\n centers[23] = (0.9016, 0.9016)\n centers[24] = (0.1334, 0.2862)\n centers[25] = (0.4085, 0.0653)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.5604436893300666}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/packing_viz.png
+ execution_time_mean: 32125.269347094
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..799450ac0ddf9786ca39e9af907df5bed39c58a7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.5604436893300666,
+ "public": {
+ "centers_str": " centers[0] = (0.4791, 0.5712)\n centers[1] = (0.2653, 0.7270)\n centers[2] = (0.6797, 0.7584)\n centers[3] = (0.6748, 0.3125)\n centers[4] = (0.4032, 0.2676)\n centers[5] = (0.2807, 0.5100)\n centers[6] = (0.8859, 0.4508)\n centers[7] = (0.0798, 0.0798)\n centers[8] = (0.6921, 0.5575)\n centers[9] = (0.5696, 0.0994)\n centers[10] = (0.9005, 0.2376)\n centers[11] = (0.9297, 0.0703)\n centers[12] = (0.5538, 0.9026)\n centers[13] = (0.3512, 0.8946)\n centers[14] = (0.5075, 0.4749)\n centers[15] = (0.7652, 0.0962)\n centers[16] = (0.0985, 0.6553)\n centers[17] = (0.4703, 0.6972)\n centers[18] = (0.7273, 0.9228)\n centers[19] = (0.2524, 0.0933)\n centers[20] = (0.0739, 0.4847)\n centers[21] = (0.1233, 0.8767)\n centers[22] = (0.8802, 0.6845)\n centers[23] = (0.9016, 0.9016)\n centers[24] = (0.1334, 0.2862)\n centers[25] = (0.4085, 0.0653)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.5604436893300666
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/packing_viz.png",
+ "execution_time_mean": 32125.269347094,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0f58c204ec0c8044bfe4f5d9f08af36cbc08e459
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/search_replace.txt
@@ -0,0 +1,93 @@
+Based on my analysis of the current simulated annealing approach and the problem's geometry, I propose two key improvements. First, I'll refine the annealing schedule and move set to encourage a longer, more thorough, and globally-aware search. Second, I will diversify the initial configurations by increasing the number of structured starts and expanding the candidate positions for the 26th circle, drawing inspiration from previously successful Memetic Algorithms. These changes aim to better explore the search space and increase the probability of discovering a superior packing configuration.
+
+
+refined_annealing_schedule
+
+
+This change adjusts the simulated annealing hyperparameters to promote a more extensive and effective search.
+- **`num_starts`** is increased from 12 to 16 to explore more regions of the solution space.
+- **`t_start`** is raised to 0.06 and **`max_step_size`** to 0.20 to facilitate more aggressive exploration at the beginning of the search, helping to break out of initial local optima.
+- **`cooling_rate`** is increased to 0.999 for a slower, more patient annealing process, allowing the system more time to settle into better configurations at each temperature.
+- **`moves_per_temp`** is slightly reduced to balance the substantially longer schedule.
+- **`move_weights`** are adjusted to `[0.70, 0.20, 0.10]`, doubling the probability of the powerful global `swap` move, which can be crucial for rearranging trapped circles.
+- **`cluster_size`** is increased to 5 to allow for slightly larger correlated movements.
+Collectively, these changes create a more robust annealing process capable of finding higher-quality solutions.
+
+
+<<<<<<< SEARCH
+ config = {
+ 'num_starts': 12, # Number of independent SA runs
+ 't_start': 0.05, # Initial temperature
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.998, # Geometric cooling factor
+ 'moves_per_temp': 60, # Number of moves at each temperature step
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+ 'cluster_size': 4,
+ }
+=======
+ config = {
+ 'num_starts': 16, # More starts to explore different basins
+ 't_start': 0.06, # Higher initial temperature for more exploration
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.999, # Slower cooling for more thorough search
+ 'moves_per_temp': 50, # Adjusted moves to balance longer schedule
+ 'max_step_size': 0.20, # Larger initial step size
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.70, 0.20, 0.10], # Increased probability for powerful swap move
+ 'cluster_size': 5, # Slightly larger clusters
+ }
+>>>>>>> REPLACE
+
+
+diversify_initial_configurations
+
+
+This edit enhances the multi-start strategy by prioritizing structured initializations and increasing their diversity. The number of grid-based starts is increased from 6 to 12 (out of 16 total starts), as these are more likely to be in a promising region of the search space than purely random configurations. Furthermore, the list of candidate positions for the 26th circle is significantly expanded. By including more strategic points—such as grid interstitial points and offset edge centers—we provide the annealer with a richer and more varied set of high-quality starting configurations, improving the chances of one of the runs converging to a global optimum.
+
+
+<<<<<<< SEARCH
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Place the 26th circle in a strategic corner or center
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+=======
+ # Prioritize structured starts (12 of 16), which are generally more promising
+ if i < 12:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Use a richer set of candidate positions for the 26th circle
+ extra_pos_candidates = [
+ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
+ [0.5, 0.5], # Center
+ [0.2, 0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8], # Grid interstitial (spacing=0.2)
+ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
+ ]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cfcfc38160c0646964c5ccd9449016b397e84285
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..9306383d5dbb8671983adb69c529a06d51537b62
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/main.py
@@ -0,0 +1,94 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place circles in a structured pattern
+ # This is a simple pattern - evolution will improve this
+
+ # First, place a large circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles around it in a ring
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
+
+ # Place 16 more circles in an outer ring
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
+
+ # Additional positioning adjustment to make sure all circles
+ # are inside the square and don't overlap
+ # Clip to ensure everything is inside the unit square
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..50ec411d681c63a2a2ea1a5d2cd94c733741238e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results
+Run 1/1 completed in 0.00 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 0.9597642169962064
+ public: {'centers_str': ' centers[0] = (0.5000, 0.5000)\n centers[1] = (0.8000, 0.5000)\n centers[2] = (0.7121, 0.7121)\n centers[3] = (0.5000, 0.8000)\n centers[4] = (0.2879, 0.7121)\n centers[5] = (0.2000, 0.5000)\n centers[6] = (0.2879, 0.2879)\n centers[7] = (0.5000, 0.2000)\n centers[8] = (0.7121, 0.2879)\n centers[9] = (0.9900, 0.5000)\n centers[10] = (0.9900, 0.7679)\n centers[11] = (0.9900, 0.9900)\n centers[12] = (0.7679, 0.9900)\n centers[13] = (0.5000, 0.9900)\n centers[14] = (0.2321, 0.9900)\n centers[15] = (0.0100, 0.9900)\n centers[16] = (0.0100, 0.7679)\n centers[17] = (0.0100, 0.5000)\n centers[18] = (0.0100, 0.2321)\n centers[19] = (0.0100, 0.0100)\n centers[20] = (0.2321, 0.0100)\n centers[21] = (0.5000, 0.0100)\n centers[22] = (0.7679, 0.0100)\n centers[23] = (0.9900, 0.0100)\n centers[24] = (0.9900, 0.2321)\n centers[25] = (0.0100, 0.0100)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 0.9597642169962064}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/packing_viz.png
+ execution_time_mean: 0.00208668177947402
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..f97dfb3c5cfe78cc7e601b464900cff843668f72
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 0.9597642169962064,
+ "public": {
+ "centers_str": " centers[0] = (0.5000, 0.5000)\n centers[1] = (0.8000, 0.5000)\n centers[2] = (0.7121, 0.7121)\n centers[3] = (0.5000, 0.8000)\n centers[4] = (0.2879, 0.7121)\n centers[5] = (0.2000, 0.5000)\n centers[6] = (0.2879, 0.2879)\n centers[7] = (0.5000, 0.2000)\n centers[8] = (0.7121, 0.2879)\n centers[9] = (0.9900, 0.5000)\n centers[10] = (0.9900, 0.7679)\n centers[11] = (0.9900, 0.9900)\n centers[12] = (0.7679, 0.9900)\n centers[13] = (0.5000, 0.9900)\n centers[14] = (0.2321, 0.9900)\n centers[15] = (0.0100, 0.9900)\n centers[16] = (0.0100, 0.7679)\n centers[17] = (0.0100, 0.5000)\n centers[18] = (0.0100, 0.2321)\n centers[19] = (0.0100, 0.0100)\n centers[20] = (0.2321, 0.0100)\n centers[21] = (0.5000, 0.0100)\n centers[22] = (0.7679, 0.0100)\n centers[23] = (0.9900, 0.0100)\n centers[24] = (0.9900, 0.2321)\n centers[25] = (0.0100, 0.0100)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 0.9597642169962064
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/packing_viz.png",
+ "execution_time_mean": 0.00208668177947402,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b5295b79b221cebf29a0900ce9186373e1e0c5c1
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..cf76d51df796bebacc56b35f2f56ba1ff7731735
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/edit.diff
@@ -0,0 +1,125 @@
+--- a/original.py
++++ b/original.py
+@@ -1,94 +1,95 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+- # Place circles in a structured pattern
+- # This is a simple pattern - evolution will improve this
++ # Place 25 circles in a 5x5 grid
++ # This provides a more even distribution and better use of space
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
+
+- # First, place a large circle in the center
+- centers[0] = [0.5, 0.5]
++ k = 0
++ for i in range(num_cells_side):
++ for j in range(num_cells_side):
++ centers[k, 0] = (i + 0.5) * spacing
++ centers[k, 1] = (j + 0.5) * spacing
++ k += 1
+
+- # Place 8 circles around it in a ring
+- for i in range(8):
+- angle = 2 * np.pi * i / 8
+- centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
++ # Place the 26th circle in a corner, slightly offset to allow for radius.
++ # This location is chosen to utilize corner space efficiently,
++ # as the grid circles are not directly in the corners.
++ centers[25] = [0.05, 0.05]
+
+- # Place 16 more circles in an outer ring
+- for i in range(16):
+- angle = 2 * np.pi * i / 16
+- centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
+-
+- # Additional positioning adjustment to make sure all circles
+- # are inside the square and don't overlap
+- # Clip to ensure everything is inside the unit square
+- centers = np.clip(centers, 0.01, 0.99)
++ # No need to clip centers, as they are intentionally placed within (0,1) range
++ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
++ # This iterative scaling is a greedy approach and the order can matter,
++ # but for constructor-based approaches, it's a common method.
++ for _ in range(100): # Iterate multiple times to stabilize radii
++ for i in range(n):
++ for j in range(i + 1, n):
++ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+- # If current radii would cause overlap
+- if radii[i] + radii[j] > dist:
+- # Scale both radii proportionally
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
++ # If current radii would cause overlap
++ if radii[i] + radii[j] > dist:
++ # Scale both radii proportionally to just touch
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
+ return radii
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..00c138de9a06c6d9335089fddee91aec818e5abd
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/main.py
@@ -0,0 +1,95 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place 25 circles in a 5x5 grid
+ # This provides a more even distribution and better use of space
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a corner, slightly offset to allow for radius.
+ # This location is chosen to utilize corner space efficiently,
+ # as the grid circles are not directly in the corners.
+ centers[25] = [0.05, 0.05]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ # This iterative scaling is a greedy approach and the order can matter,
+ # but for constructor-based approaches, it's a common method.
+ for _ in range(100): # Iterate multiple times to stabilize radii
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..f52133ed39da8e9d823b52b994ee5063132aad94
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/original.py
@@ -0,0 +1,94 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place circles in a structured pattern
+ # This is a simple pattern - evolution will improve this
+
+ # First, place a large circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles around it in a ring
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
+
+ # Place 16 more circles in an outer ring
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
+
+ # Additional positioning adjustment to make sure all circles
+ # are inside the square and don't overlap
+ # Clip to ensure everything is inside the unit square
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..1261a1358e53736d00bb4b7d5d9dc801fa7f5c4f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results
+Run 1/1 completed in 0.17 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.8730177484885533
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1000, 0.9000)\n centers[5] = (0.3000, 0.1000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.7000, 0.9000)\n centers[20] = (0.9000, 0.1000)\n centers[21] = (0.9000, 0.3000)\n centers[22] = (0.9000, 0.5000)\n centers[23] = (0.9000, 0.7000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.0500, 0.0500)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.8730177484885533}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/packing_viz.png
+ execution_time_mean: 0.17472516791895032
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..9a02f7822058634e030401415acfac31c5cf9b8a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.8730177484885533,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1000, 0.9000)\n centers[5] = (0.3000, 0.1000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.7000, 0.9000)\n centers[20] = (0.9000, 0.1000)\n centers[21] = (0.9000, 0.3000)\n centers[22] = (0.9000, 0.5000)\n centers[23] = (0.9000, 0.7000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.0500, 0.0500)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.8730177484885533
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/packing_viz.png",
+ "execution_time_mean": 0.17472516791895032,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..570da2c17053f66d83e6a4ead523c65bfff55242
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/search_replace.txt
@@ -0,0 +1,185 @@
+
+
+The current circle packing strategy relies on concentric rings, which is inefficient for a large number of circles (26). Furthermore, the `np.clip` operation with bounds `0.01` and `0.99` severely limits the radii of circles near the boundaries, leading to wasted space and tiny circles. The initial sum of radii of 0.9598 is far from the best known result of 2.635.
+
+This edit proposes two main improvements:
+
+1. **Refactor `construct_packing` for better spatial distribution:**
+ * The concentric ring pattern is replaced with a 5x5 square grid for 25 circles. This provides a more uniform and denser initial packing, where each circle in the grid starts with a potential radius of `0.1`. This alone significantly increases the sum of radii baseline (e.g., `25 * 0.1 = 2.5`).
+ * The 26th circle is explicitly placed in a corner (`[0.05, 0.05]`) slightly offset from the exact corner to allow it to have a non-zero radius. This utilizes the corner space more effectively than the previous implicit placement (`[0.0, 0.0]` clipped to `[0.01, 0.01]`).
+ * The `np.clip` line is removed. All centers are now intentionally placed strictly within the `(0,1)` range, allowing `compute_max_radii` to correctly determine their maximum radii based on boundary proximity, rather than artificially limiting them.
+
+2. **Improve `compute_max_radii` for better stabilization:**
+ * The existing `compute_max_radii` function iterates through circle pairs to resolve overlaps only once. This greedy approach can lead to suboptimal radii, as adjusting one pair might create new overlaps or alleviate existing ones that were previously handled.
+ * An outer loop is added to iterate the pairwise adjustment process multiple times (100 iterations). This allows the radii to stabilize more effectively, converging to a locally optimal solution where circles are as large as possible without overlap for the given center positions.
+
+These changes aim to drastically increase the `sum_of_radii` by providing a much more efficient initial center arrangement and improving the radius calculation accuracy.
+
+
+<<<<<<< SEARCH
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place circles in a structured pattern
+ # This is a simple pattern - evolution will improve this
+
+ # First, place a large circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles around it in a ring
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
+
+ # Place 16 more circles in an outer ring
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
+
+ # Additional positioning adjustment to make sure all circles
+ # are inside the square and don't overlap
+ # Clip to ensure everything is inside the unit square
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ return radii
+=======
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place 25 circles in a 5x5 grid
+ # This provides a more even distribution and better use of space
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a corner, slightly offset to allow for radius.
+ # This location is chosen to utilize corner space efficiently,
+ # as the grid circles are not directly in the corners.
+ centers[25] = [0.05, 0.05]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ # This iterative scaling is a greedy approach and the order can matter,
+ # but for constructor-based approaches, it's a common method.
+ for _ in range(100): # Iterate multiple times to stabilize radii
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ return radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..104e47912af53a2dbd8296d398169ad3e5bce081
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..92b76a1a15cc8c5be128e56631bc51b168d7f2de
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/edit.diff
@@ -0,0 +1,145 @@
+--- a/original.py
++++ b/original.py
+@@ -1,128 +1,124 @@
+ # EVOLVE-BLOCK-START
+ """
+ Hybrid circle packing for n=26 circles.
+ Starts with a concentric ring pattern and refines positions using
+ force-directed relaxation, then computes radii iteratively.
+ """
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method, starting from a concentric ring pattern,
+ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility.
+ np.random.seed(1337)
+
+- # 1. Initial Placement: Start with a structured concentric ring pattern.
+- # This provides a symmetric, ordered starting point for the relaxation,
+- # combining the idea from the first parent program.
++ # 1. Initial Placement: Start with a structured near-grid pattern (5-5-6-5-5 columns).
++ # This is a much better starting point than concentric circles, inspired by known optimal packings.
+ centers = np.zeros((n, 2))
++ col_counts = [5, 5, 6, 5, 5]
++ x_coords = np.linspace(0.1, 0.9, 5)
++ current_idx = 0
++ for i, count in enumerate(col_counts):
++ y_coords = np.linspace(1/(count+1), count/(count+1), count)
++ for j in range(count):
++ centers[current_idx] = [x_coords[i], y_coords[j]]
++ current_idx += 1
+
+- # Place a circle in the center
+- centers[0] = [0.5, 0.5]
+-
+- # Place 8 circles in an inner ring
+- inner_ring_radius = 0.28
+- for i in range(8):
+- angle = 2 * np.pi * i / 8
+- centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
+-
+- # Place the remaining 17 circles in an outer ring
+- outer_ring_radius = 0.48
+- for i in range(17):
+- angle = 2 * np.pi * i / 17
+- centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
++ # Add random jitter to break perfect symmetry and allow for new configurations.
++ centers += np.random.uniform(-0.02, 0.02, size=centers.shape)
++ centers = np.clip(centers, 0.01, 0.99) # Ensure they start safely inside.
+
+ # 2. Iterative Position Refinement (Force Simulation from the second parent)
+- n_iter = 250
++ n_iter = 500 # Increased iterations for better convergence
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..be506dcea9328017a2745340b6baf43a7ee79f1f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/main.py
@@ -0,0 +1,124 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid circle packing for n=26 circles.
+Starts with a concentric ring pattern and refines positions using
+force-directed relaxation, then computes radii iteratively.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method, starting from a concentric ring pattern,
+ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with a structured near-grid pattern (5-5-6-5-5 columns).
+ # This is a much better starting point than concentric circles, inspired by known optimal packings.
+ centers = np.zeros((n, 2))
+ col_counts = [5, 5, 6, 5, 5]
+ x_coords = np.linspace(0.1, 0.9, 5)
+ current_idx = 0
+ for i, count in enumerate(col_counts):
+ y_coords = np.linspace(1/(count+1), count/(count+1), count)
+ for j in range(count):
+ centers[current_idx] = [x_coords[i], y_coords[j]]
+ current_idx += 1
+
+ # Add random jitter to break perfect symmetry and allow for new configurations.
+ centers += np.random.uniform(-0.02, 0.02, size=centers.shape)
+ centers = np.clip(centers, 0.01, 0.99) # Ensure they start safely inside.
+
+ # 2. Iterative Position Refinement (Force Simulation from the second parent)
+ n_iter = 500 # Increased iterations for better convergence
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d8fb9f1a58532001a828536a4a15a44de7ef0ff
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/original.py
@@ -0,0 +1,128 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid circle packing for n=26 circles.
+Starts with a concentric ring pattern and refines positions using
+force-directed relaxation, then computes radii iteratively.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method, starting from a concentric ring pattern,
+ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with a structured concentric ring pattern.
+ # This provides a symmetric, ordered starting point for the relaxation,
+ # combining the idea from the first parent program.
+ centers = np.zeros((n, 2))
+
+ # Place a circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles in an inner ring
+ inner_ring_radius = 0.28
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
+
+ # Place the remaining 17 circles in an outer ring
+ outer_ring_radius = 0.48
+ for i in range(17):
+ angle = 2 * np.pi * i / 17
+ centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
+
+ # 2. Iterative Position Refinement (Force Simulation from the second parent)
+ n_iter = 250
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..46625a353dae99e2f2d153c762bf82ae41742401
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results
+Run 1/1 completed in 0.04 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.0573677714897678
+ public: {'centers_str': ' centers[0] = (0.0207, 0.0142)\n centers[1] = (0.0213, 0.0841)\n centers[2] = (0.0270, 0.4135)\n centers[3] = (0.0186, 0.8366)\n centers[4] = (0.0152, 0.9810)\n centers[5] = (0.1593, 0.0219)\n centers[6] = (0.0253, 0.2238)\n centers[7] = (0.0260, 0.6139)\n centers[8] = (0.1475, 0.8927)\n centers[9] = (0.1040, 0.9841)\n centers[10] = (0.6268, 0.0232)\n centers[11] = (0.3613, 0.0240)\n centers[12] = (0.5167, 0.2242)\n centers[13] = (0.4951, 0.7061)\n centers[14] = (0.6248, 0.9756)\n centers[15] = (0.3783, 0.9764)\n centers[16] = (0.8370, 0.0219)\n centers[17] = (0.9785, 0.0840)\n centers[18] = (0.9707, 0.5939)\n centers[19] = (0.9721, 0.7911)\n centers[20] = (0.8395, 0.9785)\n centers[21] = (0.9790, 0.0141)\n centers[22] = (0.9747, 0.2268)\n centers[23] = (0.9731, 0.4266)\n centers[24] = (0.9789, 0.9190)\n centers[25] = (0.9791, 0.9863)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.0573677714897678}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/packing_viz.png
+ execution_time_mean: 0.04305929783731699
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..bbffdcfe0381c86fa33602c0631564ea68a40971
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.0573677714897678,
+ "public": {
+ "centers_str": " centers[0] = (0.0207, 0.0142)\n centers[1] = (0.0213, 0.0841)\n centers[2] = (0.0270, 0.4135)\n centers[3] = (0.0186, 0.8366)\n centers[4] = (0.0152, 0.9810)\n centers[5] = (0.1593, 0.0219)\n centers[6] = (0.0253, 0.2238)\n centers[7] = (0.0260, 0.6139)\n centers[8] = (0.1475, 0.8927)\n centers[9] = (0.1040, 0.9841)\n centers[10] = (0.6268, 0.0232)\n centers[11] = (0.3613, 0.0240)\n centers[12] = (0.5167, 0.2242)\n centers[13] = (0.4951, 0.7061)\n centers[14] = (0.6248, 0.9756)\n centers[15] = (0.3783, 0.9764)\n centers[16] = (0.8370, 0.0219)\n centers[17] = (0.9785, 0.0840)\n centers[18] = (0.9707, 0.5939)\n centers[19] = (0.9721, 0.7911)\n centers[20] = (0.8395, 0.9785)\n centers[21] = (0.9790, 0.0141)\n centers[22] = (0.9747, 0.2268)\n centers[23] = (0.9731, 0.4266)\n centers[24] = (0.9789, 0.9190)\n centers[25] = (0.9791, 0.9863)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.0573677714897678
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/packing_viz.png",
+ "execution_time_mean": 0.04305929783731699,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4236f7ed86478f60dd0c290012801da648aa86cf
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/search_replace.txt
@@ -0,0 +1,60 @@
+
+grid_based_initialization
+
+
+The current initial placement strategy, based on concentric rings, leads to a suboptimal configuration dominated by a large central circle, resulting in a low sum of radii (1.28). This is a common local minimum for packing problems.
+
+To achieve a more efficient packing, I am replacing the concentric ring initialization with a structured, non-uniform grid pattern. Specifically, I will arrange the 26 circles into 5 columns with counts of [5, 5, 6, 5, 5]. This pattern is inspired by the structures of known optimal packings for similar `n` values. It provides a much denser and more promising starting configuration for the relaxation algorithm by breaking the central symmetry and distributing circles more evenly. I've also added a small amount of random jitter to this grid to help the force-directed algorithm explore variations and avoid getting stuck in a rigid grid structure. This should guide the solver towards a significantly better region of the solution space.
+
+
+<<<<<<< SEARCH
+ # 1. Initial Placement: Start with a structured concentric ring pattern.
+ # This provides a symmetric, ordered starting point for the relaxation,
+ # combining the idea from the first parent program.
+ centers = np.zeros((n, 2))
+
+ # Place a circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles in an inner ring
+ inner_ring_radius = 0.28
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
+
+ # Place the remaining 17 circles in an outer ring
+ outer_ring_radius = 0.48
+ for i in range(17):
+ angle = 2 * np.pi * i / 17
+ centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
+=======
+ # 1. Initial Placement: Start with a structured near-grid pattern (5-5-6-5-5 columns).
+ # This is a much better starting point than concentric circles, inspired by known optimal packings.
+ centers = np.zeros((n, 2))
+ col_counts = [5, 5, 6, 5, 5]
+ x_coords = np.linspace(0.1, 0.9, 5)
+ current_idx = 0
+ for i, count in enumerate(col_counts):
+ y_coords = np.linspace(1/(count+1), count/(count+1), count)
+ for j in range(count):
+ centers[current_idx] = [x_coords[i], y_coords[j]]
+ current_idx += 1
+
+ # Add random jitter to break perfect symmetry and allow for new configurations.
+ centers += np.random.uniform(-0.02, 0.02, size=centers.shape)
+ centers = np.clip(centers, 0.01, 0.99) # Ensure they start safely inside.
+>>>>>>> REPLACE
+
+
+increase_relaxation_iterations
+
+
+I am increasing the number of iterations for the force-directed relaxation from 250 to 500. The new initial configuration is more complex and will benefit from a longer relaxation time to allow the circles to settle into a better-optimized final arrangement. More iterations reduce the chance of stopping prematurely in a suboptimal state.
+
+
+<<<<<<< SEARCH
+ n_iter = 250
+=======
+ n_iter = 500 # Increased iterations for better convergence
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f53a18ac4166b49af1f6f68c54d1aff97c0505db
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..060600588ddbe00c417c5535fba23c0c2a63df92
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/edit.diff
@@ -0,0 +1,508 @@
+--- a/original.py
++++ b/original.py
+@@ -1,306 +1,250 @@
+ # EVOLVE-BLOCK-START
+-"""
+-This solution implements a Memetic Algorithm (MA), which combines a Genetic
+-Algorithm (GA) for global exploration with a powerful, physics-based local search
+-for exploitation. This approach hybridizes the successful population-based
+-framework of prior submissions with the refined, two-stage force-directed
+-simulation from the current program, which will be repurposed as the local
+-search operator.
+-"""
+-
+ import numpy as np
+-
+-class LocalSearchRefiner:
+- """
+- Performs a fast, two-stage, force-directed local refinement on a set of centers.
+- This is adapted from the full simulation of the parent program to act as a
+- powerful local search operator within the Memetic Algorithm.
++import math
++import random
++
++def compute_max_radii(centers, max_iter=800, convergence_threshold=1e-8):
++ """
++ Compute maximum radii using iterative proportional scaling.
++ This is a robust version with a stable convergence check.
++ """
++ n = centers.shape[0]
++ if n == 0:
++ return np.array([])
++
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ required_converged_iters = 3
++ converged_in_a_row = 0
++
++ for _ in range(max_iter):
++ old_radii = radii.copy()
++
++ for i in range(n):
++ for j in range(i + 1, n):
++ dist_sq = np.sum((centers[i] - centers[j])**2)
++ dist = np.sqrt(dist_sq)
++
++ if radii[i] + radii[j] > dist:
++ if dist < 1e-9:
++ radii[i], radii[j] = 0.0, 0.0
++ else:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++
++ max_abs_change = np.max(np.abs(radii - old_radii))
++ if max_abs_change < convergence_threshold:
++ converged_in_a_row += 1
++ if converged_in_a_row >= required_converged_iters:
++ break
++ else:
++ converged_in_a_row = 0
++
++ return np.maximum(radii, 0)
++
++
++class QuickRefiner:
++ """
++ A fast, aggressive, single-phase local search operator for memetic injection.
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+- """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+- n = refined_centers.shape[0]
+-
+- # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+- for sim_iter in range(self.config['lsr_aggressive_iter']):
+- progress = sim_iter / self.config['lsr_aggressive_iter']
+- current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+- current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+- (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+-
+- radii = compute_max_radii(refined_centers)
++
++ for sim_iter in range(self.config['refiner_iter']):
++ progress = sim_iter / self.config['refiner_iter']
++ current_lr = self.config['refiner_lr'] * (1.0 - progress)**2
++ current_pressure = self.config['refiner_pressure_end'] + \
++ (self.config['refiner_pressure_start'] - self.config['refiner_pressure_end']) * (1.0 - progress)**2
++
++ # Fast radius calculation for simulation
++ radii = compute_max_radii(refined_centers, max_iter=100)
+ inflated_radii = radii * current_pressure
+
++ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
++
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+- wall_strength = self.config['lsr_wall_strength']
++ wall_strength = self.config['refiner_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+-
+- # --- Stage 2: Fine-tuning with subtle growth pressure ---
+- for sim_iter_ft in range(self.config['lsr_finetune_iter']):
+- progress = sim_iter_ft / self.config['lsr_finetune_iter']
+- current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
+- current_pressure = self.config['lsr_finetune_pressure_end'] + \
+- (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+-
+- radii = compute_max_radii(refined_centers)
+- inflated_radii = radii * current_pressure
+-
+- diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+-
+- sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+- overlaps = np.maximum(0, sum_radii_pairs - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+-
+- wall_forces = np.zeros_like(refined_centers)
+- wall_strength = self.config['lsr_wall_strength']
+- wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+- wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+-
+- forces = circle_forces + wall_forces
+- refined_centers += forces * current_lr
+- refined_centers = np.clip(refined_centers, 0.0, 1.0)
+-
++
+ return refined_centers
+
+-class MemeticAlgorithm:
+- """Encapsulates the entire Memetic Algorithm for circle packing."""
+- def __init__(self, n, config):
++
++class SimulatedAnnealerWithInjection:
++ """
++ Performs a search using SA, with a memetic local search integrated as a move operator.
++ """
++ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+- self.population = []
+- self.fitnesses = np.array([])
+- self.best_solution = None
+- self.best_fitness = -1.0
+- self.local_search_refiner = LocalSearchRefiner(config=config)
+-
+- def _initialize_population(self):
+- """Initializes a diverse population with both random and grid-based individuals."""
+- num_grid_based = self.config['population_size'] // 3
+-
+- for i in range(self.config['population_size']):
+- if i < num_grid_based:
+- centers = np.zeros((self.n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for row in range(num_cells_side):
+- for col in range(num_cells_side):
+- centers[k, 0] = (col + 0.5) * spacing
+- centers[k, 1] = (row + 0.5) * spacing
+- k += 1
+- centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+-
+- # Place 26th circle in a random-ish interstitial void
+- interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
+- extra_pos = interstitial_points[i % len(interstitial_points)]
+- centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
+- self.population.append(np.clip(centers, 0.0, 1.0))
+- else:
+- self.population.append(np.random.rand(self.n, 2))
+-
+- def _evaluate_population(self):
+- """Calculates fitness for the population and updates the best solution."""
+- fitnesses = []
+- for ind in self.population:
+- radii = compute_max_radii(ind)
+- fitnesses.append(np.sum(radii))
+- self.fitnesses = np.array(fitnesses)
+-
+- best_idx = np.argmax(self.fitnesses)
+- if self.fitnesses[best_idx] > self.best_fitness:
+- self.best_fitness = self.fitnesses[best_idx]
+- self.best_solution = self.population[best_idx].copy()
+-
+- def _select_parent(self):
+- """Selects a parent using tournament selection."""
+- tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+- tourn_fitnesses = self.fitnesses[tourn_indices]
+- winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+- return self.population[winner_idx]
+-
+- def _crossover(self, p1, p2):
+- """Performs blend crossover (BLX-alpha)."""
+- alpha = self.config['crossover_alpha']
+- child = alpha * p1 + (1.0 - alpha) * p2
+- return np.clip(child, 0.0, 1.0)
+-
+- def _mutate(self, individual, strength):
+- """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+- mutated_ind = individual.copy()
+- mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+- noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+- mutated_ind[mutation_mask] += noise
+-
+- if np.random.rand() < self.config['jump_mutation_prob']:
+- idx_to_reset = np.random.randint(0, self.n)
+- mutated_ind[idx_to_reset] = np.random.rand(2)
+-
+- return np.clip(mutated_ind, 0.0, 1.0)
++ self.centers = initial_centers
++
++ self.temp = config['t_start']
++ self.max_step_size = config['max_step_size']
++
++ self.energy = self._calculate_energy(self.centers)
++ self.best_centers = self.centers.copy()
++ self.best_energy = self.energy
++
++ self.move_weights = config['move_weights']
++ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles, self._refinement_injection]
++
++ self.refiner = QuickRefiner(config)
++
++ def _calculate_energy(self, centers):
++ """Energy is the negative sum of radii, to be minimized."""
++ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
++ return -np.sum(radii)
++
++ def _propose_move(self):
++ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
++ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
++ current_step_size = self.max_step_size * t_progress
++ return move_func(current_step_size)
++
++ def _move_single_circle(self, step_size):
++ new_centers = self.centers.copy()
++ idx = random.randint(0, self.n - 1)
++ displacement = np.random.normal(0, step_size, size=2)
++ new_centers[idx] += displacement
++ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
++ return new_centers
++
++ def _move_cluster(self, step_size):
++ new_centers = self.centers.copy()
++ cluster_size = self.config['cluster_size']
++ seed_idx = random.randint(0, self.n - 1)
++ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
++ neighbor_indices = np.argsort(dists)[:cluster_size]
++ displacement = np.random.normal(0, step_size * 0.5, size=2)
++ new_centers[neighbor_indices] += displacement
++ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
++ return new_centers
++
++ def _swap_circles(self, step_size):
++ new_centers = self.centers.copy()
++ if self.n < 2: return new_centers
++ idx1, idx2 = random.sample(range(self.n), 2)
++ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
++ return new_centers
++
++ def _refinement_injection(self, step_size):
++ """Applies the quick local search as a single, powerful move."""
++ return self.refiner.refine(self.centers)
+
+ def run(self):
+- """Executes the full memetic algorithm evolution."""
+- self._initialize_population()
+-
+- for gen in range(self.config['generations']):
+- self._evaluate_population()
+-
+- new_population = []
+-
+- # Elitism: carry over best individuals
+- elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+- for idx in elite_indices:
+- new_population.append(self.population[idx].copy())
+-
+- # Anneal mutation strength
+- mut_strength = self.config['mut_strength_end'] + \
+- (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+- (1.0 - (gen / self.config['generations']))**2.0
+-
+- while len(new_population) < self.config['population_size']:
+- p1 = self._select_parent()
+- p2 = self._select_parent()
+-
+- child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+- child = self._mutate(child, mut_strength)
+-
+- # Memetic step: Apply local search probabilistically
+- if np.random.rand() < self.config['local_search_prob']:
+- child = self.local_search_refiner.refine(child)
+-
+- new_population.append(child)
+-
+- self.population = new_population
+-
+- self._evaluate_population()
+- return self.best_solution
++ """Executes the simulated annealing search."""
++ while self.temp > self.config['t_end']:
++ for _ in range(self.config['moves_per_temp']):
++ new_centers = self._propose_move()
++ new_energy = self._calculate_energy(new_centers)
++
++ delta_e = new_energy - self.energy
++
++ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
++ self.centers = new_centers
++ self.energy = new_energy
++
++ if self.energy < self.best_energy:
++ self.best_energy = self.energy
++ self.best_centers = self.centers.copy()
++
++ self.temp *= self.config['cooling_rate']
++
++ return self.best_centers
++
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles using a Memetic Algorithm.
++ Constructs a packing of 26 circles using a multi-start SA with memetic injection.
+ """
+ n = 26
+ config = {
+- # --- GA Parameters ---
+- 'population_size': 80,
+- 'generations': 350,
+- 'elite_count': 4,
+- 'tournament_size': 6,
+- 'mutation_rate': 0.35, # Per-circle mutation probability
+- 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
+- 'mut_strength_start': 0.1,
+- 'mut_strength_end': 0.001,
+- 'crossover_rate': 0.9,
+- 'crossover_alpha': 0.5, # For BLX-alpha crossover
+- # --- Memetic Parameters ---
+- 'local_search_prob': 0.20, # Probability of applying local search to a new child
+- # --- Local Search Refiner (LSR) Parameters ---
+- 'lsr_aggressive_iter': 40,
+- 'lsr_aggressive_lr': 0.025,
+- 'lsr_aggressive_pressure_start': 1.06,
+- 'lsr_aggressive_pressure_end': 1.001,
+- 'lsr_finetune_iter': 80,
+- 'lsr_finetune_lr': 0.001,
+- 'lsr_finetune_pressure_start': 1.0008,
+- 'lsr_finetune_pressure_end': 1.00001,
+- 'lsr_wall_strength': 0.5,
++ # --- Multi-Start Config ---
++ 'num_starts': 12,
++ # --- SA Config ---
++ 't_start': 0.05,
++ 't_end': 1e-6,
++ 'cooling_rate': 0.9985, # Slower cooling for deeper search
++ 'moves_per_temp': 80,
++ 'max_step_size': 0.15,
++ 'radius_iter': 250,
++ 'move_weights': [0.60, 0.20, 0.15, 0.05], # [single, cluster, swap, refine]
++ 'cluster_size': 4,
++ # --- Refiner Config (for injection) ---
++ 'refiner_iter': 50,
++ 'refiner_lr': 0.02,
++ 'refiner_pressure_start': 1.05,
++ 'refiner_pressure_end': 1.001,
++ 'refiner_wall_strength': 0.5,
+ }
+
+- solver = MemeticAlgorithm(n=n, config=config)
+- best_centers = solver.run()
+-
+- # Final, high-precision radius calculation for the best solution
+- final_radii = compute_max_radii(best_centers)
+-
+- return best_centers, final_radii
+-
+-
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This uses an iterative proportional scaling method.
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- max_radius_iter = 600 # Maximum iterations for radius calculation
+- convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+- required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+- converged_in_a_row = 0 # Counter for consecutive converged iterations
+-
+- for iteration in range(max_radius_iter):
+- previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+-
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9: # Centers are practically identical
+- radii[i] = 0.0
+- radii[j] = 0.0
+- # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- # Calculate the maximum absolute change in any radius
+- max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+-
+- # Check for convergence based on max_absolute_change
+- if max_absolute_change < convergence_epsilon:
+- converged_in_a_row += 1
+- if converged_in_a_row >= required_converged_iters:
+- break # Converged for several consecutive iterations
++ best_overall_centers = None
++ best_overall_score = -1.0
++
++ for i in range(config['num_starts']):
++ initial_centers = np.zeros((n, 2))
++ # --- Strategic Initialization (from best prior SA) ---
++ if i < config['num_starts'] // 2:
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for row in range(num_cells_side):
++ for col in range(num_cells_side):
++ initial_centers[k, 0] = (col + 0.5) * spacing
++ initial_centers[k, 1] = (row + 0.5) * spacing
++ k += 1
++ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
++
++ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
++ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
++ initial_centers = np.clip(initial_centers, 0.01, 0.99)
+ else:
+- converged_in_a_row = 0 # Reset counter if change is significant
+-
+- return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
++ initial_centers = np.random.rand(n, 2)
++
++ solver = SimulatedAnnealerWithInjection(n=n, initial_centers=initial_centers, config=config)
++ result_centers = solver.run()
++
++ radii = compute_max_radii(result_centers, max_iter=2500, convergence_threshold=1e-9)
++ score = np.sum(radii)
++
++ if score > best_overall_score:
++ best_overall_score = score
++ best_overall_centers = result_centers
++
++ final_radii = compute_max_radii(best_overall_centers, max_iter=4000, convergence_threshold=1e-9)
++
++ return best_overall_centers, final_radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..53e99690615173262ef922dc9da564874ddffb35
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/main.py
@@ -0,0 +1,250 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=800, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This is a robust version with a stable convergence check.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ required_converged_iters = 3
+ converged_in_a_row = 0
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ max_abs_change = np.max(np.abs(radii - old_radii))
+ if max_abs_change < convergence_threshold:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break
+ else:
+ converged_in_a_row = 0
+
+ return np.maximum(radii, 0)
+
+
+class QuickRefiner:
+ """
+ A fast, aggressive, single-phase local search operator for memetic injection.
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ refined_centers = centers.copy()
+
+ for sim_iter in range(self.config['refiner_iter']):
+ progress = sim_iter / self.config['refiner_iter']
+ current_lr = self.config['refiner_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['refiner_pressure_end'] + \
+ (self.config['refiner_pressure_start'] - self.config['refiner_pressure_end']) * (1.0 - progress)**2
+
+ # Fast radius calculation for simulation
+ radii = compute_max_radii(refined_centers, max_iter=100)
+ inflated_radii = radii * current_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['refiner_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class SimulatedAnnealerWithInjection:
+ """
+ Performs a search using SA, with a memetic local search integrated as a move operator.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles, self._refinement_injection]
+
+ self.refiner = QuickRefiner(config)
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+ seed_idx = random.randint(0, self.n - 1)
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+ displacement = np.random.normal(0, step_size * 0.5, size=2)
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def _refinement_injection(self, step_size):
+ """Applies the quick local search as a single, powerful move."""
+ return self.refiner.refine(self.centers)
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ new_centers = self._propose_move()
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start SA with memetic injection.
+ """
+ n = 26
+ config = {
+ # --- Multi-Start Config ---
+ 'num_starts': 12,
+ # --- SA Config ---
+ 't_start': 0.05,
+ 't_end': 1e-6,
+ 'cooling_rate': 0.9985, # Slower cooling for deeper search
+ 'moves_per_temp': 80,
+ 'max_step_size': 0.15,
+ 'radius_iter': 250,
+ 'move_weights': [0.60, 0.20, 0.15, 0.05], # [single, cluster, swap, refine]
+ 'cluster_size': 4,
+ # --- Refiner Config (for injection) ---
+ 'refiner_iter': 50,
+ 'refiner_lr': 0.02,
+ 'refiner_pressure_start': 1.05,
+ 'refiner_pressure_end': 1.001,
+ 'refiner_wall_strength': 0.5,
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ for i in range(config['num_starts']):
+ initial_centers = np.zeros((n, 2))
+ # --- Strategic Initialization (from best prior SA) ---
+ if i < config['num_starts'] // 2:
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.01, 0.99)
+ else:
+ initial_centers = np.random.rand(n, 2)
+
+ solver = SimulatedAnnealerWithInjection(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ radii = compute_max_radii(result_centers, max_iter=2500, convergence_threshold=1e-9)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ final_radii = compute_max_radii(best_overall_centers, max_iter=4000, convergence_threshold=1e-9)
+
+ return best_overall_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..6d95001722637c0a9df674b7dd049669085ab9e8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/original.py
@@ -0,0 +1,306 @@
+# EVOLVE-BLOCK-START
+"""
+This solution implements a Memetic Algorithm (MA), which combines a Genetic
+Algorithm (GA) for global exploration with a powerful, physics-based local search
+for exploitation. This approach hybridizes the successful population-based
+framework of prior submissions with the refined, two-stage force-directed
+simulation from the current program, which will be repurposed as the local
+search operator.
+"""
+
+import numpy as np
+
+class LocalSearchRefiner:
+ """
+ Performs a fast, two-stage, force-directed local refinement on a set of centers.
+ This is adapted from the full simulation of the parent program to act as a
+ powerful local search operator within the Memetic Algorithm.
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ n = refined_centers.shape[0]
+
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
+ progress = sim_iter / self.config['lsr_aggressive_iter']
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning with subtle growth pressure ---
+ for sim_iter_ft in range(self.config['lsr_finetune_iter']):
+ progress = sim_iter_ft / self.config['lsr_finetune_iter']
+ current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
+ current_pressure = self.config['lsr_finetune_pressure_end'] + \
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 3
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+ # Place 26th circle in a random-ish interstitial void
+ interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ fitnesses = []
+ for ind in self.population:
+ radii = compute_max_radii(ind)
+ fitnesses.append(np.sum(radii))
+ self.fitnesses = np.array(fitnesses)
+
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs blend crossover (BLX-alpha)."""
+ alpha = self.config['crossover_alpha']
+ child = alpha * p1 + (1.0 - alpha) * p2
+ return np.clip(child, 0.0, 1.0)
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over best individuals
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # --- GA Parameters ---
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 4,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.35, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'crossover_alpha': 0.5, # For BLX-alpha crossover
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ # --- Local Search Refiner (LSR) Parameters ---
+ 'lsr_aggressive_iter': 40,
+ 'lsr_aggressive_lr': 0.025,
+ 'lsr_aggressive_pressure_start': 1.06,
+ 'lsr_aggressive_pressure_end': 1.001,
+ 'lsr_finetune_iter': 80,
+ 'lsr_finetune_lr': 0.001,
+ 'lsr_finetune_pressure_start': 1.0008,
+ 'lsr_finetune_pressure_end': 1.00001,
+ 'lsr_wall_strength': 0.5,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers)
+
+ return best_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..61f7e213b505427d7486a91862f94f6475422161
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results/job_log.err
@@ -0,0 +1,19 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
+Traceback (most recent call last):
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/evaluate.py", line 302, in
+ main(parsed_args.program_path, parsed_args.results_dir)
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/evaluate.py", line 262, in main
+ metrics, correct, error_msg = run_shinka_eval(
+ ^^^^^^^^^^^^^^^^
+ File "/home/tengxiao/pj/ShinkaEvolve/shinka/core/wrap_eval.py", line 119, in run_shinka_eval
+ run_result = experiment_fn(**kwargs)
+ ^^^^^^^^^^^^^^^^^^^^^^^
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/main.py", line 247, in run_packing
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..8205f50f27d04d146197abba4bed8355ed6f10d1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results/job_log.out
@@ -0,0 +1,2 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ac9a4f9d45d2f34477663f2109484a8e651e4ec6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/rewrite.txt
@@ -0,0 +1,241 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=800, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This is a robust version with a stable convergence check.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ required_converged_iters = 3
+ converged_in_a_row = 0
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ max_abs_change = np.max(np.abs(radii - old_radii))
+ if max_abs_change < convergence_threshold:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break
+ else:
+ converged_in_a_row = 0
+
+ return np.maximum(radii, 0)
+
+
+class QuickRefiner:
+ """
+ A fast, aggressive, single-phase local search operator for memetic injection.
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ refined_centers = centers.copy()
+
+ for sim_iter in range(self.config['refiner_iter']):
+ progress = sim_iter / self.config['refiner_iter']
+ current_lr = self.config['refiner_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['refiner_pressure_end'] + \
+ (self.config['refiner_pressure_start'] - self.config['refiner_pressure_end']) * (1.0 - progress)**2
+
+ # Fast radius calculation for simulation
+ radii = compute_max_radii(refined_centers, max_iter=100)
+ inflated_radii = radii * current_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['refiner_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class SimulatedAnnealerWithInjection:
+ """
+ Performs a search using SA, with a memetic local search integrated as a move operator.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles, self._refinement_injection]
+
+ self.refiner = QuickRefiner(config)
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+ seed_idx = random.randint(0, self.n - 1)
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+ displacement = np.random.normal(0, step_size * 0.5, size=2)
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def _refinement_injection(self, step_size):
+ """Applies the quick local search as a single, powerful move."""
+ return self.refiner.refine(self.centers)
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ new_centers = self._propose_move()
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start SA with memetic injection.
+ """
+ n = 26
+ config = {
+ # --- Multi-Start Config ---
+ 'num_starts': 12,
+ # --- SA Config ---
+ 't_start': 0.05,
+ 't_end': 1e-6,
+ 'cooling_rate': 0.9985, # Slower cooling for deeper search
+ 'moves_per_temp': 80,
+ 'max_step_size': 0.15,
+ 'radius_iter': 250,
+ 'move_weights': [0.60, 0.20, 0.15, 0.05], # [single, cluster, swap, refine]
+ 'cluster_size': 4,
+ # --- Refiner Config (for injection) ---
+ 'refiner_iter': 50,
+ 'refiner_lr': 0.02,
+ 'refiner_pressure_start': 1.05,
+ 'refiner_pressure_end': 1.001,
+ 'refiner_wall_strength': 0.5,
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ for i in range(config['num_starts']):
+ initial_centers = np.zeros((n, 2))
+ # --- Strategic Initialization (from best prior SA) ---
+ if i < config['num_starts'] // 2:
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.01, 0.99)
+ else:
+ initial_centers = np.random.rand(n, 2)
+
+ solver = SimulatedAnnealerWithInjection(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ radii = compute_max_radii(result_centers, max_iter=2500, convergence_threshold=1e-9)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ final_radii = compute_max_radii(best_overall_centers, max_iter=4000, convergence_threshold=1e-9)
+
+ return best_overall_centers, final_radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a7634b4b5a3851fb315215d2047e0518aa7e8b32
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..6b6c766248979a92be8e588591e3a7a195b1f330
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/edit.diff
@@ -0,0 +1,457 @@
+--- a/original.py
++++ b/original.py
+@@ -1,283 +1,315 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
++ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+-
++
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
++ # Calculate distances and handle overlaps for all pairs
++ # Vectorized calculation for distances to improve performance
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++
++ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
++ dist = dists[i, j] # Pre-calculated distance
++
+ if radii[i] + radii[j] > dist:
+- if dist < 1e-9:
+- radii[i], radii[j] = 0.0, 0.0
++ if dist < 1e-9: # Handle co-located centers: radii must be zero
++ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
++ radii[i], radii[j] = 0.0, 0.0
++ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+- updated = True
+-
++ updated = True
++
++ # Check for convergence: if no updates were made AND the max change is tiny.
++ # This prevents infinite loops if changes are smaller than convergence_threshold
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+-
++
+ return np.maximum(radii, 0)
+
+
+-class LocalSearchRefiner:
+- """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+- def __init__(self, config):
++class HybridLocalSearcher:
++ """
++ A powerful local search refiner based on a two-phase physical simulation.
++ This approach incorporates a "growth pressure" concept for exploration.
++ """
++ def __init__(self, n, config):
++ self.n = n
+ self.config = config
+
++ def _compute_radii_for_sim(self, centers):
++ """A fast radius computation for use inside the simulation loop."""
++ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
++
+ def refine(self, centers):
+- """Applies a two-stage force-directed refinement to polish a solution."""
++ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+- # --- Stage 1: Aggressive settling with subtle growth pressure ---
+- for i_agg_iter in range(self.config['ls_aggressive_iter']):
+- progress = i_agg_iter / self.config['ls_aggressive_iter']
+- current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+- (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+- (1.0 - progress)**1.5 # Cubic decay for growth pressure
+-
+- radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+- pressured_radii = radii * current_growth_pressure
+-
+- # Vectorized Force Calculation
++ # Phase 1: Main refinement with decreasing growth pressure
++ iterations = self.config['sim_iter']
++ base_lr = self.config['learning_rate']
++ wall_strength = self.config['wall_strength']
++ initial_growth_pressure = self.config['initial_growth_pressure']
++ final_growth_pressure = self.config['final_growth_pressure']
++
++ for i_iter in range(iterations):
++ progress = i_iter / iterations
++ # Anneal growth pressure quadratically
++ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++ current_radii = self._compute_radii_for_sim(refined_centers)
++ pressured_radii = current_radii * current_growth_pressure
++
++ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+-
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+-
+- force_mags = overlaps
+- force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+- wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+- wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+-
++ # Wall forces calculation
++ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
++
+ forces = circle_forces + wall_forces
+-
+- current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+- (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+- (1.0 - progress)**2.0 # Quadratic decay for LR
+-
+- refined_centers += forces * current_lr_agg
++ # Anneal learning rate quadratically
++ lr = base_lr * (1.0 - progress)**2
++ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+- # --- Stage 2: Fine-tuning without growth pressure ---
+- for _ in range(self.config['ls_fine_tune_iter']):
+- radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+-
+- # Vectorized Force Calculation (no growth pressure)
++ # Phase 2: Fine-tuning with minimal growth pressure
++ iterations_ft = self.config['fine_tune_iter']
++ lr_ft = self.config['fine_tune_lr']
++ for _ in range(iterations_ft):
++ current_radii = self._compute_radii_for_sim(refined_centers)
++
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+-
+- radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
++ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+-
+- force_mags = overlaps
+- force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+- wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+- wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+-
++ # Wall forces calculation
++ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
++
+ forces = circle_forces + wall_forces
+-
+- refined_centers += forces * self.config['ls_fine_tune_lr']
++ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+ class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+- self.local_search_refiner = LocalSearchRefiner(config=config)
++ # Use the HybridLocalSearcher with its specific config
++ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+- """Initializes a diverse population with both random and grid-based individuals."""
+- num_grid_based = self.config['population_size'] // 4
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- interstitial_points = [
+- [spacing, spacing], [spacing, 1 - spacing],
+- [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+- ]
+-
+- for i in range(self.config['population_size']):
+- if i < num_grid_based:
+- centers = np.zeros((self.n, 2))
+- k = 0
+- for row in range(num_cells_side):
+- for col in range(num_cells_side):
+- centers[k, 0] = (col + 0.5) * spacing
+- centers[k, 1] = (row + 0.5) * spacing
+- k += 1
+- centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+-
+- # Place 26th circle in one of the strategic voids
+- extra_pos = interstitial_points[i % len(interstitial_points)]
+- centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+- self.population.append(np.clip(centers, 0.0, 1.0))
+- else:
+- self.population.append(np.random.rand(self.n, 2))
+-
++ """Initializes a diverse population using strategic grid-based starts and random individuals."""
++ spacing = 1.0 / 5
++ # The explicit list of candidate positions for the 26th circle, from a high-performing prior
++ candidate_extra_positions = self.config['initial_candidates']
++
++ # Base for 25 circles in a 5x5 grid, to be perturbed
++ base_centers_25 = np.zeros((self.n - 1, 2))
++ k = 0
++ for j in range(5):
++ for i in range(5):
++ base_centers_25[k, 0] = (i + 0.5) * spacing
++ base_centers_25[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
++
++ # Add strategically initialized configurations
++ for i in range(num_strategic_starts):
++ centers = np.zeros((self.n, 2))
++ # Apply perturbation to break symmetry
++ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers_25.shape)
++ perturbed_base_centers = np.clip(base_centers_25 + perturbation, 0.0, 1.0)
++ centers[:self.n-1] = perturbed_base_centers
++ # Place the 26th circle at a strategic point
++ centers[self.n-1] = np.array(candidate_extra_positions[i])
++ self.population.append(centers)
++
++ # Add purely random configurations to ensure broad exploration
++ num_random_starts = self.config['population_size'] - len(self.population)
++ for _ in range(num_random_starts):
++ self.population.append(np.random.rand(self.n, 2))
++
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+-
++
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+- """
+- Performs Blend Crossover (BLX-alpha) on continuous variables.
+- Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+- """
+- # Using a small random range around 0.5 for alpha for averaging blend
+- alpha = np.random.uniform(0.4, 0.6)
+- child = alpha * p1 + (1 - alpha) * p2
+- return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
++ """Performs uniform crossover, taking whole circles from either parent."""
++ child = p1.copy()
++ mask = np.random.rand(self.n) < 0.5
++ child[mask] = p2[mask]
++ return child
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+- """Applies Gaussian mutation and a chance of a strong mutation."""
++ """Applies Gaussian mutation and a chance of a strong 'jump' mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+-
+- if np.random.rand() < 0.02: # 2% chance of a jump mutation
++
++ if np.random.rand() < self.config['jump_mutation_prob']: # Jump mutation probability from a good prior
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+-
++
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+-
++
+ new_population = []
+-
+- # Elitism
++
++ # Elitism: carry over the best individuals directly
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+-
+- # Anneal mutation strength
++
++ # Anneal mutation strength over generations with a quadratic decay
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+- (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+-
+- current_mutation_rate = self.config['mutation_rate_end'] + \
+- (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+- (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
++ (1.0 - (gen / self.config['generations']))**2.0
++
++ # Anneal mutation rate per circle over generations with a cubic decay
++ current_mutation_rate_per_circle = self.config['mutation_rate_end_per_circle'] + \
++ (self.config['mutation_rate_start_per_circle'] - self.config['mutation_rate_end_per_circle']) * \
++ (1.0 - (gen / self.config['generations']))**1.5
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+-
++
++ # Perform crossover, or just copy a parent if crossover doesn't happen
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+- child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+-
+- # Memetic step: Apply local search probabilistically
++ # Apply mutation with annealed strength and rate
++ child = self._mutate(child, mut_strength, current_mutation_rate_per_circle)
++
++ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+-
++
+ new_population.append(child)
+-
++
+ self.population = new_population
+-
++
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
++ spacing = 1.0 / 5
+ config = {
++ # MA parameters
+ 'population_size': 80, # Increased population size for more diversity
+- 'generations': 400, # Increased generations for more thorough evolution
+- 'elite_count': 4,
+- 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+- 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+- 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+- 'mut_strength_start': 0.1,
+- 'mut_strength_end': 0.001,
+- 'crossover_rate': 0.9,
+- # Memetic Parameters
+- 'local_search_prob': 0.15, # Probability of applying local search to a new child
+-
+- # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+- 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+- 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+- 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+- 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+- 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+- 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+-
+- 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+- 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+- 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+- 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
++ 'generations': 350, # Sufficient generations for thorough evolution
++ 'elite_count': 5, # Number of elite individuals to carry over
++ 'tournament_size': 6, # Tournament size for selection pressure
++ 'mutation_rate_start_per_circle': 0.45, # Initial probability for a circle to mutate
++ 'mutation_rate_end_per_circle': 0.05, # Final probability after annealing
++ 'mut_strength_start': 0.1, # Initial strength of Gaussian mutation
++ 'mut_strength_end': 0.001, # Final strength of Gaussian mutation
++ 'crossover_rate': 0.9, # Probability of performing crossover
++ 'jump_mutation_prob': 0.03, # Probability of a strong "jump" mutation for a circle
++
++ # Initialization parameters for _initialize_population
++ 'initial_perturbation_scale': 0.02, # Scale for perturbing grid-based initial positions
++ 'initial_candidates': [ # Strategic points for the 26th circle
++ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
++ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
++ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
++ ],
++
++ # Memetic Parameters (Local Search, passed to HybridLocalSearcher)
++ 'local_search_prob': 0.20, # Probability of applying local search to a new child
++ 'ls_config': { # Configuration for the HybridLocalSearcher
++ 'sim_iter': 500, # Iterations for the main simulation phase
++ 'radius_sim_iter': 150, # Radii calculation iterations during simulation
++ 'learning_rate': 0.018, # Base learning rate for center updates
++ 'wall_strength': 0.65, # Strength of repulsion from square walls
++ 'initial_growth_pressure': 1.05, # Initial multiplier for radii to create expansion pressure
++ 'final_growth_pressure': 1.001, # Final multiplier for radii
++ 'fine_tune_iter': 250, # Iterations for the fine-tuning phase
++ 'fine_tune_lr': 0.0015, # Learning rate for the fine-tuning phase
++ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+-
++
+ # Final, high-precision radius calculation for the best solution
+- final_radii = compute_max_radii(best_centers, max_iter=1500)
+-
++ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
++
+ return best_centers, final_radii
+-
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..318cf111d430fb99c72101c10e3134b858a63013
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/main.py
@@ -0,0 +1,315 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ # Calculate distances and handle overlaps for all pairs
+ # Vectorized calculation for distances to improve performance
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ # This prevents infinite loops if changes are smaller than convergence_threshold
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This approach incorporates a "growth pressure" concept for exploration.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ # Wall forces calculation
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ # Anneal learning rate quadratically
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ # Wall forces calculation
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ # Use the HybridLocalSearcher with its specific config
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
+ spacing = 1.0 / 5
+ # The explicit list of candidate positions for the 26th circle, from a high-performing prior
+ candidate_extra_positions = self.config['initial_candidates']
+
+ # Base for 25 circles in a 5x5 grid, to be perturbed
+ base_centers_25 = np.zeros((self.n - 1, 2))
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ base_centers_25[k, 0] = (i + 0.5) * spacing
+ base_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+
+ # Add strategically initialized configurations
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Apply perturbation to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers_25.shape)
+ perturbed_base_centers = np.clip(base_centers_25 + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Place the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # Add purely random configurations to ensure broad exploration
+ num_random_starts = self.config['population_size'] - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong 'jump' mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']: # Jump mutation probability from a good prior
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals directly
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength over generations with a quadratic decay
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ # Anneal mutation rate per circle over generations with a cubic decay
+ current_mutation_rate_per_circle = self.config['mutation_rate_end_per_circle'] + \
+ (self.config['mutation_rate_start_per_circle'] - self.config['mutation_rate_end_per_circle']) * \
+ (1.0 - (gen / self.config['generations']))**1.5
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ # Perform crossover, or just copy a parent if crossover doesn't happen
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ # Apply mutation with annealed strength and rate
+ child = self._mutate(child, mut_strength, current_mutation_rate_per_circle)
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 350, # Sufficient generations for thorough evolution
+ 'elite_count': 5, # Number of elite individuals to carry over
+ 'tournament_size': 6, # Tournament size for selection pressure
+ 'mutation_rate_start_per_circle': 0.45, # Initial probability for a circle to mutate
+ 'mutation_rate_end_per_circle': 0.05, # Final probability after annealing
+ 'mut_strength_start': 0.1, # Initial strength of Gaussian mutation
+ 'mut_strength_end': 0.001, # Final strength of Gaussian mutation
+ 'crossover_rate': 0.9, # Probability of performing crossover
+ 'jump_mutation_prob': 0.03, # Probability of a strong "jump" mutation for a circle
+
+ # Initialization parameters for _initialize_population
+ 'initial_perturbation_scale': 0.02, # Scale for perturbing grid-based initial positions
+ 'initial_candidates': [ # Strategic points for the 26th circle
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search, passed to HybridLocalSearcher)
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ 'ls_config': { # Configuration for the HybridLocalSearcher
+ 'sim_iter': 500, # Iterations for the main simulation phase
+ 'radius_sim_iter': 150, # Radii calculation iterations during simulation
+ 'learning_rate': 0.018, # Base learning rate for center updates
+ 'wall_strength': 0.65, # Strength of repulsion from square walls
+ 'initial_growth_pressure': 1.05, # Initial multiplier for radii to create expansion pressure
+ 'final_growth_pressure': 1.001, # Final multiplier for radii
+ 'fine_tune_iter': 250, # Iterations for the fine-tuning phase
+ 'fine_tune_lr': 0.0015, # Learning rate for the fine-tuning phase
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..e53e50b4f5ba23507c3bd0b593eab9b77ba8edad
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/original.py
@@ -0,0 +1,283 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+
+ # --- Stage 1: Aggressive settling with subtle growth pressure ---
+ for i_agg_iter in range(self.config['ls_aggressive_iter']):
+ progress = i_agg_iter / self.config['ls_aggressive_iter']
+ current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+ (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+ (1.0 - progress)**1.5 # Cubic decay for growth pressure
+
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+ pressured_radii = radii * current_growth_pressure
+
+ # Vectorized Force Calculation
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+ (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+ (1.0 - progress)**2.0 # Quadratic decay for LR
+
+ refined_centers += forces * current_lr_agg
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning without growth pressure ---
+ for _ in range(self.config['ls_fine_tune_iter']):
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+
+ # Vectorized Force Calculation (no growth pressure)
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.config['ls_fine_tune_lr']
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+
+ current_mutation_rate = self.config['mutation_rate_end'] + \
+ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 400, # Increased generations for more thorough evolution
+ 'elite_count': 4,
+ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+
+ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+
+ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..25ee53d5fd9a70186ff3806f8f474ab47cc259e1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results
+Run 1/1 completed in 2045.39 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.3900820406051997
+ public: {'centers_str': ' centers[0] = (0.0728, 0.0726)\n centers[1] = (0.3990, 0.0827)\n centers[2] = (0.5807, 0.2651)\n centers[3] = (0.7525, 0.0642)\n centers[4] = (0.9098, 0.0905)\n centers[5] = (0.0960, 0.2383)\n centers[6] = (0.2359, 0.0877)\n centers[7] = (0.5973, 0.7393)\n centers[8] = (0.7753, 0.2323)\n centers[9] = (0.9382, 0.2695)\n centers[10] = (0.0714, 0.4039)\n centers[11] = (0.3449, 0.3151)\n centers[12] = (0.4616, 0.5669)\n centers[13] = (0.6203, 0.3970)\n centers[14] = (0.8670, 0.4547)\n centers[15] = (0.0333, 0.7805)\n centers[16] = (0.1791, 0.6286)\n centers[17] = (0.4664, 0.7655)\n centers[18] = (0.6826, 0.6316)\n centers[19] = (0.8942, 0.7135)\n centers[20] = (0.1022, 0.8980)\n centers[21] = (0.3127, 0.8912)\n centers[22] = (0.5009, 0.9258)\n centers[23] = (0.6991, 0.8763)\n centers[24] = (0.9101, 0.9095)\n centers[25] = (0.5686, 0.1243)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.3900820406051997}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/packing_viz.png
+ execution_time_mean: 2045.3863690267317
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..a4627bd1fa6fc499908ba4a909bf103bc44834e0
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.3900820406051997,
+ "public": {
+ "centers_str": " centers[0] = (0.0728, 0.0726)\n centers[1] = (0.3990, 0.0827)\n centers[2] = (0.5807, 0.2651)\n centers[3] = (0.7525, 0.0642)\n centers[4] = (0.9098, 0.0905)\n centers[5] = (0.0960, 0.2383)\n centers[6] = (0.2359, 0.0877)\n centers[7] = (0.5973, 0.7393)\n centers[8] = (0.7753, 0.2323)\n centers[9] = (0.9382, 0.2695)\n centers[10] = (0.0714, 0.4039)\n centers[11] = (0.3449, 0.3151)\n centers[12] = (0.4616, 0.5669)\n centers[13] = (0.6203, 0.3970)\n centers[14] = (0.8670, 0.4547)\n centers[15] = (0.0333, 0.7805)\n centers[16] = (0.1791, 0.6286)\n centers[17] = (0.4664, 0.7655)\n centers[18] = (0.6826, 0.6316)\n centers[19] = (0.8942, 0.7135)\n centers[20] = (0.1022, 0.8980)\n centers[21] = (0.3127, 0.8912)\n centers[22] = (0.5009, 0.9258)\n centers[23] = (0.6991, 0.8763)\n centers[24] = (0.9101, 0.9095)\n centers[25] = (0.5686, 0.1243)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.3900820406051997
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/packing_viz.png",
+ "execution_time_mean": 2045.3863690267317,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c5ad752151251cebfbc0da463f691945b7ca912f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/rewrite.txt
@@ -0,0 +1,306 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ # Calculate distances and handle overlaps for all pairs
+ # Vectorized calculation for distances to improve performance
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ # This prevents infinite loops if changes are smaller than convergence_threshold
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This approach incorporates a "growth pressure" concept for exploration.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ # Wall forces calculation
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ # Anneal learning rate quadratically
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ # Wall forces calculation
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ # Use the HybridLocalSearcher with its specific config
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
+ spacing = 1.0 / 5
+ # The explicit list of candidate positions for the 26th circle, from a high-performing prior
+ candidate_extra_positions = self.config['initial_candidates']
+
+ # Base for 25 circles in a 5x5 grid, to be perturbed
+ base_centers_25 = np.zeros((self.n - 1, 2))
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ base_centers_25[k, 0] = (i + 0.5) * spacing
+ base_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+
+ # Add strategically initialized configurations
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Apply perturbation to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers_25.shape)
+ perturbed_base_centers = np.clip(base_centers_25 + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Place the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # Add purely random configurations to ensure broad exploration
+ num_random_starts = self.config['population_size'] - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong 'jump' mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']: # Jump mutation probability from a good prior
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals directly
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength over generations with a quadratic decay
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ # Anneal mutation rate per circle over generations with a cubic decay
+ current_mutation_rate_per_circle = self.config['mutation_rate_end_per_circle'] + \
+ (self.config['mutation_rate_start_per_circle'] - self.config['mutation_rate_end_per_circle']) * \
+ (1.0 - (gen / self.config['generations']))**1.5
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ # Perform crossover, or just copy a parent if crossover doesn't happen
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ # Apply mutation with annealed strength and rate
+ child = self._mutate(child, mut_strength, current_mutation_rate_per_circle)
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 350, # Sufficient generations for thorough evolution
+ 'elite_count': 5, # Number of elite individuals to carry over
+ 'tournament_size': 6, # Tournament size for selection pressure
+ 'mutation_rate_start_per_circle': 0.45, # Initial probability for a circle to mutate
+ 'mutation_rate_end_per_circle': 0.05, # Final probability after annealing
+ 'mut_strength_start': 0.1, # Initial strength of Gaussian mutation
+ 'mut_strength_end': 0.001, # Final strength of Gaussian mutation
+ 'crossover_rate': 0.9, # Probability of performing crossover
+ 'jump_mutation_prob': 0.03, # Probability of a strong "jump" mutation for a circle
+
+ # Initialization parameters for _initialize_population
+ 'initial_perturbation_scale': 0.02, # Scale for perturbing grid-based initial positions
+ 'initial_candidates': [ # Strategic points for the 26th circle
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search, passed to HybridLocalSearcher)
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ 'ls_config': { # Configuration for the HybridLocalSearcher
+ 'sim_iter': 500, # Iterations for the main simulation phase
+ 'radius_sim_iter': 150, # Radii calculation iterations during simulation
+ 'learning_rate': 0.018, # Base learning rate for center updates
+ 'wall_strength': 0.65, # Strength of repulsion from square walls
+ 'initial_growth_pressure': 1.05, # Initial multiplier for radii to create expansion pressure
+ 'final_growth_pressure': 1.001, # Final multiplier for radii
+ 'fine_tune_iter': 250, # Iterations for the fine-tuning phase
+ 'fine_tune_lr': 0.0015, # Learning rate for the fine-tuning phase
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..acaec7d8f7c68962bd51532bbd2681913fdc70f8
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..7505467aab8c11ab9aed58d01ba5a7cc8f5fa841
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/edit.diff
@@ -0,0 +1,395 @@
+--- a/original.py
++++ b/original.py
+@@ -1,308 +1,333 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+-def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+- """
+- Compute maximum radii using iterative proportional scaling with a convergence check.
+- Vectorized calculation of initial radii based on wall distance.
++def compute_max_radii(centers):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+- radii = np.min([
+- centers[:, 0], 1 - centers[:, 0],
+- centers[:, 1], 1 - centers[:, 1]
+- ], axis=0)
+-
+- # Iterative scaling for inter-circle distances
+- for _ in range(max_iter):
+- old_radii = radii.copy()
+- updated = False
++ radii = np.ones(n)
++
++ # Initialize radii to distance to nearest wall
++ for i in range(n):
++ x, y = centers[i]
++ radii[i] = min(x, y, 1 - x, 1 - y)
++
++ max_radius_iter = 600 # Maximum iterations for radius calculation
++ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
++ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
++ converged_in_a_row = 0 # Counter for consecutive converged iterations
++
++ for iteration in range(max_radius_iter):
++ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
++
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
++
++ if dist < 1e-9: # Centers are practically identical
++ radii[i] = 0.0
++ radii[j] = 0.0
++ continue
++
+ if radii[i] + radii[j] > dist:
+- if dist < 1e-9: # Handle co-located centers
+- radii[i], radii[j] = 0.0, 0.0
+- else:
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+- updated = True
+-
+- # Check for convergence
+- if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+- break
+-
+- return np.maximum(radii, 0)
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++
++ # Calculate the maximum absolute change in any radius
++ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
++
++ # Check for convergence based on max_absolute_change
++ if max_absolute_change < convergence_epsilon:
++ converged_in_a_row += 1
++ if converged_in_a_row >= required_converged_iters:
++ break # Converged for several consecutive iterations
++ else:
++ converged_in_a_row = 0 # Reset counter if change is significant
++
++ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+
+
+ class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+- This replaces simpler force-directed approaches by incorporating a "growth pressure"
+- concept adapted from the current program's core logic.
++ This incorporates a "growth pressure" concept to drive circles into tighter
++ configurations while resolving overlaps.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+- return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
++ # Use specific iterations and threshold for simulation's radius calculation
++ return compute_max_radii(centers, max_radius_iter=self.config['radius_sim_iter'], convergence_epsilon=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # --- Phase 1: Main refinement with decreasing growth pressure ---
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ lr = base_lr * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9
++ dists[dists < 1e-9] = 1e-9 # Avoid division by zero
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-tuning with minimal growth pressure ---
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+ class MemeticAlgorithm:
+ """
+ Combines a Genetic Algorithm for global exploration with a powerful local search meme
+ for exploitation, addressing the limitations of a pure multi-start local search.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """
+ Initializes a diverse population using perturbed grid-based starts and random individuals.
+ The perturbation is critical for breaking symmetry and escaping the local optima of a perfect grid.
+ """
+ pop_size = self.config['population_size']
+
+ # --- Strategic Grid-Based Initialization ---
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ grid_dim = 5
+ spacing = 1.0 / grid_dim
+ for j in range(grid_dim):
+ for i in range(grid_dim):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = self.config['initial_candidates']
+ num_strategic_starts = min(len(candidate_extra_positions), pop_size // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Critical step: Add perturbation to the base grid to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Add the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # --- Random Initialization ---
+ num_random_starts = pop_size - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best overall solution."""
+ fitnesses = [np.sum(compute_max_radii(ind)) for ind in self.population]
+ self.fitnesses = np.array(fitnesses)
+ best_idx_current_gen = np.argmax(self.fitnesses)
+
+ if self.fitnesses[best_idx_current_gen] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx_current_gen]
+ self.best_solution = self.population[best_idx_current_gen].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+- """Performs uniform crossover, swapping entire circles between parents."""
+- child = p1.copy()
+- mask = np.random.rand(self.n) < 0.5
+- child[mask] = p2[mask]
+- return child
+-
+- def _mutate(self, individual, strength):
+- """Applies Gaussian mutation and a small chance of a large "jump" mutation."""
++ """
++ Performs Blend Crossover (BLX-alpha) on continuous variables.
++ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
++ """
++ # Using a small random range around 0.5 for alpha for averaging blend
++ alpha = np.random.uniform(0.4, 0.6)
++ child = alpha * p1 + (1 - alpha) * p2
++ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
++
++ def _mutate(self, individual, strength, mutation_probability):
++ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+- mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
++ mutation_mask = np.random.rand(self.n) < mutation_probability # Use annealed probability
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals from the current population
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+- # Anneal mutation strength over generations
++ # Anneal mutation strength and probability over generations
+ progress = gen / self.config['generations']
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+- (1.0 - progress)**2.0
++ (1.0 - progress)**2.0 # Quadratic decay for strength
++
++ current_mutation_probability = self.config['mut_prob_end'] + \
++ (self.config['mut_prob_start'] - self.config['mut_prob_end']) * \
++ (1.0 - progress)**1.5 # Cubic decay for probability
+
+ # Generate the rest of the new population through selection, crossover, mutation, and local search
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+- child = self._mutate(child, mut_strength)
++ child = self._mutate(child, mut_strength, current_mutation_probability) # Pass annealed probability
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to capture the best from the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by evolving a population of solutions
+ using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+- 'population_size': 80,
+- 'generations': 350,
+- 'elite_count': 5,
+- 'tournament_size': 6,
+- 'mutation_rate': 0.45, # Per-circle mutation probability
+- 'jump_mutation_prob': 0.03, # Chance to reset one circle's position
+- 'mut_strength_start': 0.1,
++ 'population_size': 100, # Increased for more diversity
++ 'generations': 400, # Increased for longer search
++ 'elite_count': 6, # Preserve more good solutions
++ 'tournament_size': 7, # Stronger selection pressure
++ 'mut_prob_start': 0.4, # Initial per-circle mutation probability
++ 'mut_prob_end': 0.05, # Final per-circle mutation probability (annealed)
++ 'jump_mutation_prob': 0.03, # Chance to reset one circle's position to a random spot
++ 'mut_strength_start': 0.12, # Slightly higher initial mutation displacement
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+
+- # Initialization parameters
++ # Initialization parameters (retained as good)
+ 'initial_perturbation_scale': 0.02,
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search)
+- 'local_search_prob': 0.20,
++ 'local_search_prob': 0.25, # Increased probability for local search
+ 'ls_config': {
+- 'sim_iter': 500,
+- 'radius_sim_iter': 150,
+- 'learning_rate': 0.018,
+- 'wall_strength': 0.65,
+- 'initial_growth_pressure': 1.05,
++ 'sim_iter': 600, # More iterations for aggressive phase
++ 'radius_sim_iter': 120, # Slightly faster radius calc during simulation
++ 'learning_rate': 0.02, # Slightly more aggressive learning rate
++ 'wall_strength': 0.7, # Slightly stronger wall repulsion
++ 'initial_growth_pressure': 1.06, # More aggressive initial pushing
+ 'final_growth_pressure': 1.001,
+- 'fine_tune_iter': 250,
+- 'fine_tune_lr': 0.0015,
++ 'fine_tune_iter': 300, # More iterations for fine-tuning
++ 'fine_tune_lr': 0.001, # Even finer learning rate for last stage
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best found solution
+- final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
++ final_radii = compute_max_radii(best_centers, max_radius_iter=2000, convergence_epsilon=1e-9, required_converged_iters=10)
+
+ return best_centers, final_radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..c9511f6a385b321fbd265ad100303639572e2da6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/main.py
@@ -0,0 +1,333 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # Initialize radii to distance to nearest wall
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This incorporates a "growth pressure" concept to drive circles into tighter
+ configurations while resolving overlaps.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ # Use specific iterations and threshold for simulation's radius calculation
+ return compute_max_radii(centers, max_radius_iter=self.config['radius_sim_iter'], convergence_epsilon=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # --- Phase 1: Main refinement with decreasing growth pressure ---
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ lr = base_lr * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Avoid division by zero
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-tuning with minimal growth pressure ---
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """
+ Combines a Genetic Algorithm for global exploration with a powerful local search meme
+ for exploitation, addressing the limitations of a pure multi-start local search.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """
+ Initializes a diverse population using perturbed grid-based starts and random individuals.
+ The perturbation is critical for breaking symmetry and escaping the local optima of a perfect grid.
+ """
+ pop_size = self.config['population_size']
+
+ # --- Strategic Grid-Based Initialization ---
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ grid_dim = 5
+ spacing = 1.0 / grid_dim
+ for j in range(grid_dim):
+ for i in range(grid_dim):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = self.config['initial_candidates']
+ num_strategic_starts = min(len(candidate_extra_positions), pop_size // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Critical step: Add perturbation to the base grid to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Add the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # --- Random Initialization ---
+ num_random_starts = pop_size - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best overall solution."""
+ fitnesses = [np.sum(compute_max_radii(ind)) for ind in self.population]
+ self.fitnesses = np.array(fitnesses)
+ best_idx_current_gen = np.argmax(self.fitnesses)
+
+ if self.fitnesses[best_idx_current_gen] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx_current_gen]
+ self.best_solution = self.population[best_idx_current_gen].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+ def _mutate(self, individual, strength, mutation_probability):
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < mutation_probability # Use annealed probability
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals from the current population
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength and probability over generations
+ progress = gen / self.config['generations']
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - progress)**2.0 # Quadratic decay for strength
+
+ current_mutation_probability = self.config['mut_prob_end'] + \
+ (self.config['mut_prob_start'] - self.config['mut_prob_end']) * \
+ (1.0 - progress)**1.5 # Cubic decay for probability
+
+ # Generate the rest of the new population through selection, crossover, mutation, and local search
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_probability) # Pass annealed probability
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to capture the best from the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by evolving a population of solutions
+ using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+ 'population_size': 100, # Increased for more diversity
+ 'generations': 400, # Increased for longer search
+ 'elite_count': 6, # Preserve more good solutions
+ 'tournament_size': 7, # Stronger selection pressure
+ 'mut_prob_start': 0.4, # Initial per-circle mutation probability
+ 'mut_prob_end': 0.05, # Final per-circle mutation probability (annealed)
+ 'jump_mutation_prob': 0.03, # Chance to reset one circle's position to a random spot
+ 'mut_strength_start': 0.12, # Slightly higher initial mutation displacement
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+
+ # Initialization parameters (retained as good)
+ 'initial_perturbation_scale': 0.02,
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search)
+ 'local_search_prob': 0.25, # Increased probability for local search
+ 'ls_config': {
+ 'sim_iter': 600, # More iterations for aggressive phase
+ 'radius_sim_iter': 120, # Slightly faster radius calc during simulation
+ 'learning_rate': 0.02, # Slightly more aggressive learning rate
+ 'wall_strength': 0.7, # Slightly stronger wall repulsion
+ 'initial_growth_pressure': 1.06, # More aggressive initial pushing
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 300, # More iterations for fine-tuning
+ 'fine_tune_lr': 0.001, # Even finer learning rate for last stage
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best found solution
+ final_radii = compute_max_radii(best_centers, max_radius_iter=2000, convergence_epsilon=1e-9, required_converged_iters=10)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a3deffc2b4eecf82fdbf349098c380192642cad
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/original.py
@@ -0,0 +1,308 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ Vectorized calculation of initial radii based on wall distance.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iterative scaling for inter-circle distances
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This replaces simpler force-directed approaches by incorporating a "growth pressure"
+ concept adapted from the current program's core logic.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # --- Phase 1: Main refinement with decreasing growth pressure ---
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ lr = base_lr * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-tuning with minimal growth pressure ---
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """
+ Combines a Genetic Algorithm for global exploration with a powerful local search meme
+ for exploitation, addressing the limitations of a pure multi-start local search.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """
+ Initializes a diverse population using perturbed grid-based starts and random individuals.
+ The perturbation is critical for breaking symmetry and escaping the local optima of a perfect grid.
+ """
+ pop_size = self.config['population_size']
+
+ # --- Strategic Grid-Based Initialization ---
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ grid_dim = 5
+ spacing = 1.0 / grid_dim
+ for j in range(grid_dim):
+ for i in range(grid_dim):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = self.config['initial_candidates']
+ num_strategic_starts = min(len(candidate_extra_positions), pop_size // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Critical step: Add perturbation to the base grid to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Add the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # --- Random Initialization ---
+ num_random_starts = pop_size - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best overall solution."""
+ fitnesses = [np.sum(compute_max_radii(ind)) for ind in self.population]
+ self.fitnesses = np.array(fitnesses)
+ best_idx_current_gen = np.argmax(self.fitnesses)
+
+ if self.fitnesses[best_idx_current_gen] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx_current_gen]
+ self.best_solution = self.population[best_idx_current_gen].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, swapping entire circles between parents."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a small chance of a large "jump" mutation."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals from the current population
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength over generations
+ progress = gen / self.config['generations']
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - progress)**2.0
+
+ # Generate the rest of the new population through selection, crossover, mutation, and local search
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to capture the best from the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by evolving a population of solutions
+ using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 5,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.45, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.03, # Chance to reset one circle's position
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+
+ # Initialization parameters
+ 'initial_perturbation_scale': 0.02,
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search)
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+ 'sim_iter': 500,
+ 'radius_sim_iter': 150,
+ 'learning_rate': 0.018,
+ 'wall_strength': 0.65,
+ 'initial_growth_pressure': 1.05,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best found solution
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..1a4abe9a2db5b6e2b354a1a4b07a4fb6c206bf84
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": false,
+ "error": "compute_max_radii() got an unexpected keyword argument 'max_radius_iter'"
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..09ae951068681b163e68f2ba3a6e758ddf5ce165
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/job_log.out
@@ -0,0 +1,14 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results
+Evaluation error: compute_max_radii() got an unexpected keyword argument 'max_radius_iter'
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/metrics.json
+Evaluation or Validation failed: compute_max_radii() got an unexpected keyword argument 'max_radius_iter'
+Metrics:
+ combined_score: 0.0
+ execution_time_mean: 0.0
+ execution_time_std: 0.0
+ num_successful_runs: 0
+ num_valid_runs: 0
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..fec2021a6a036637c9d3049ae9778c77cef78421
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/results/metrics.json
@@ -0,0 +1,9 @@
+{
+ "combined_score": 0.0,
+ "execution_time_mean": 0.0,
+ "execution_time_std": 0.0,
+ "num_successful_runs": 0,
+ "num_valid_runs": 0,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2be36eaae0c229c9b1e055cc866126b1f06bb50b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/rewrite.txt
@@ -0,0 +1,324 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # Initialize radii to distance to nearest wall
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This incorporates a "growth pressure" concept to drive circles into tighter
+ configurations while resolving overlaps.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ # Use specific iterations and threshold for simulation's radius calculation
+ return compute_max_radii(centers, max_radius_iter=self.config['radius_sim_iter'], convergence_epsilon=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # --- Phase 1: Main refinement with decreasing growth pressure ---
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ lr = base_lr * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Avoid division by zero
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-tuning with minimal growth pressure ---
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """
+ Combines a Genetic Algorithm for global exploration with a powerful local search meme
+ for exploitation, addressing the limitations of a pure multi-start local search.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """
+ Initializes a diverse population using perturbed grid-based starts and random individuals.
+ The perturbation is critical for breaking symmetry and escaping the local optima of a perfect grid.
+ """
+ pop_size = self.config['population_size']
+
+ # --- Strategic Grid-Based Initialization ---
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ grid_dim = 5
+ spacing = 1.0 / grid_dim
+ for j in range(grid_dim):
+ for i in range(grid_dim):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = self.config['initial_candidates']
+ num_strategic_starts = min(len(candidate_extra_positions), pop_size // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Critical step: Add perturbation to the base grid to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Add the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # --- Random Initialization ---
+ num_random_starts = pop_size - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best overall solution."""
+ fitnesses = [np.sum(compute_max_radii(ind)) for ind in self.population]
+ self.fitnesses = np.array(fitnesses)
+ best_idx_current_gen = np.argmax(self.fitnesses)
+
+ if self.fitnesses[best_idx_current_gen] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx_current_gen]
+ self.best_solution = self.population[best_idx_current_gen].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+ def _mutate(self, individual, strength, mutation_probability):
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < mutation_probability # Use annealed probability
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals from the current population
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength and probability over generations
+ progress = gen / self.config['generations']
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - progress)**2.0 # Quadratic decay for strength
+
+ current_mutation_probability = self.config['mut_prob_end'] + \
+ (self.config['mut_prob_start'] - self.config['mut_prob_end']) * \
+ (1.0 - progress)**1.5 # Cubic decay for probability
+
+ # Generate the rest of the new population through selection, crossover, mutation, and local search
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_probability) # Pass annealed probability
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to capture the best from the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by evolving a population of solutions
+ using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+ 'population_size': 100, # Increased for more diversity
+ 'generations': 400, # Increased for longer search
+ 'elite_count': 6, # Preserve more good solutions
+ 'tournament_size': 7, # Stronger selection pressure
+ 'mut_prob_start': 0.4, # Initial per-circle mutation probability
+ 'mut_prob_end': 0.05, # Final per-circle mutation probability (annealed)
+ 'jump_mutation_prob': 0.03, # Chance to reset one circle's position to a random spot
+ 'mut_strength_start': 0.12, # Slightly higher initial mutation displacement
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+
+ # Initialization parameters (retained as good)
+ 'initial_perturbation_scale': 0.02,
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search)
+ 'local_search_prob': 0.25, # Increased probability for local search
+ 'ls_config': {
+ 'sim_iter': 600, # More iterations for aggressive phase
+ 'radius_sim_iter': 120, # Slightly faster radius calc during simulation
+ 'learning_rate': 0.02, # Slightly more aggressive learning rate
+ 'wall_strength': 0.7, # Slightly stronger wall repulsion
+ 'initial_growth_pressure': 1.06, # More aggressive initial pushing
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 300, # More iterations for fine-tuning
+ 'fine_tune_lr': 0.001, # Even finer learning rate for last stage
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best found solution
+ final_radii = compute_max_radii(best_centers, max_radius_iter=2000, convergence_epsilon=1e-9, required_converged_iters=10)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..9ed783d6874e49ff12157557a8d238a5de8666a3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/edit.diff
@@ -0,0 +1,341 @@
+--- a/original.py
++++ b/original.py
+@@ -1,318 +1,332 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ # --- Core Utility: Radius Calculation ---
+ # Adopted from constructor; more robust with convergence check.
+ def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+ # --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+ class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+ # --- GA Core Components ---
+ class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+ def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles: # For n=26, this places the 26th circle at index 25
+ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+
+ # Add the base grid configuration
+ population.append(Individual(grid_centers_base.copy(), n_circles))
+
+ # Add several perturbed versions of the grid for more diverse starting points
+ for _ in range(population_size // 4): # Approximately 25% of the population
+ perturbed_centers = grid_centers_base + np.random.normal(0, 0.015, (n_circles, 2)) # Increased perturbation strength
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+ def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+ def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+ def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+- Applies Gaussian noise mutation to individual circle centers.
+- Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+- perturbed by Gaussian noise with `mutation_strength` standard deviation.
++ Applies an enhanced mutation operator with multiple strategies:
++ 1. Standard Gaussian Jitter (for local fine-tuning).
++ 2. Swap Mutation (for large-scale topological changes).
++ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+- mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+-
+- # Apply Gaussian noise only to selected circles
++
++ # --- Strategy 1: Standard Gaussian Jitter (always applied) ---
++ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
++
++ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
++ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
++ if n_circles > 1:
++ i, j = np.random.choice(n_circles, 2, replace=False)
++ # Swap the centers
++ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
++
++ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
++ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
++ # Pick one random circle and move it to a new random location.
++ idx_to_reinit = np.random.randint(0, n_circles)
++ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+ # --- Main Orchestrator: Memetic Algorithm ---
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 100 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 750 # Total number of evolutionary steps
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..856f3ad15a0ff6cd03c98767a690e673bc6792e1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/main.py
@@ -0,0 +1,332 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles: # For n=26, this places the 26th circle at index 25
+ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+
+ # Add the base grid configuration
+ population.append(Individual(grid_centers_base.copy(), n_circles))
+
+ # Add several perturbed versions of the grid for more diverse starting points
+ for _ in range(population_size // 4): # Approximately 25% of the population
+ perturbed_centers = grid_centers_base + np.random.normal(0, 0.015, (n_circles, 2)) # Increased perturbation strength
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter (always applied) ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ # Swap the centers
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 100 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 750 # Total number of evolutionary steps
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..0ba881f936d1106d846688771ed727d7d791c6ae
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/original.py
@@ -0,0 +1,318 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles: # For n=26, this places the 26th circle at index 25
+ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+
+ # Add the base grid configuration
+ population.append(Individual(grid_centers_base.copy(), n_circles))
+
+ # Add several perturbed versions of the grid for more diverse starting points
+ for _ in range(population_size // 4): # Approximately 25% of the population
+ perturbed_centers = grid_centers_base + np.random.normal(0, 0.015, (n_circles, 2)) # Increased perturbation strength
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies Gaussian noise mutation to individual circle centers.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+
+ # Apply Gaussian noise only to selected circles
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 100 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 750 # Total number of evolutionary steps
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d466990521dc283d1ca4edf9332101edeff1986d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_103/search_replace.txt
@@ -0,0 +1,65 @@
+
+enhanced_mutation_operators
+
+
+
+The current mutation operator only applies a simple Gaussian jitter to circle centers. This is effective for local refinement but weak for escaping deep local optima or exploring topologically different configurations.
+
+This edit enhances the `mutate` function by incorporating two additional, more powerful mutation strategies alongside the existing Gaussian jitter:
+
+1. **Swap Mutation**: With a 10% probability, the positions of two randomly selected circles are swapped. This is a large-scale move that can drastically alter the packing's topology, helping the algorithm to explore fundamentally different arrangements and escape from entrenched local minima.
+
+2. **Re-initialize Mutation**: With a 5% probability, a single randomly chosen circle is moved to a new, completely random position within the unit square. This acts as a "reset" for a single circle, providing a chance to break it out of a poorly optimized location (e.g., being a tiny circle stuck between larger ones) and find a more spacious region.
+
+The original Gaussian jitter is still applied, ensuring continuous fine-tuning. By adding these probabilistic large-scale moves, the mutation operator becomes a more versatile tool for exploration, increasing the algorithm's chances of discovering higher-quality packing configurations beyond what simple local search and crossover can achieve.
+
+
+
+<<<<<<< SEARCH
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies Gaussian noise mutation to individual circle centers.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+
+ # Apply Gaussian noise only to selected circles
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+=======
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter (always applied) ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ # Swap the centers
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..20d9f3dbc575bdb6a8d5d2e88f3b1d9aee1e19ad
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..73cd7a433eb3e6caa12bc0e4c92bac415b63224c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/edit.diff
@@ -0,0 +1,646 @@
+--- a/original.py
++++ b/original.py
+@@ -1,307 +1,360 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
+-
+ import numpy as np
+
+-
+-def construct_packing():
+- """
+- Constructs a specific arrangement of 26 circles in a unit square
+- by trying multiple initial placements for the 26th circle
+- and refining each using a two-phase force-directed simulation.
+- The best resulting packing (highest sum of radii) is then returned.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- n = 26
+-
+- # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+- sim_iter = 2500 # Increased iterations for more thorough refinement.
+- learning_rate = 0.02 # A balanced learning rate for exploration.
+- wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+- initial_growth_pressure = 1.04 # Increased pressure to better 'unfreeze' the grid.
+- final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+- fine_tune_iter = 600 # Increased iterations for final settlement.
+- fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+- radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+- final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+- num_trials_per_candidate = 3 # Number of simulations per candidate starting position.
+-
+- # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
++# --- Core Utility: Radius Calculation ---
++def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ This version includes a convergence check for robustness and efficiency.
++ """
++ n = centers.shape[0]
++ # Initial radii are limited by wall distance
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ for _ in range(max_iter):
++ updated_in_pass = False
++ previous_radii = radii.copy()
++
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers[i] - centers[j]
++ dist = np.linalg.norm(vec)
++
++ if dist < 1e-9: # If centers are almost co-located
++ if radii[i] > 0 or radii[j] > 0:
++ radii[i], radii[j] = 0.0, 0.0
++ updated_in_pass = True
++ continue
++
++ if radii[i] + radii[j] > dist:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated_in_pass = True
++
++ # Check for convergence after a full pass over all pairs
++ max_delta_r = np.max(np.abs(radii - previous_radii))
++ if not updated_in_pass and max_delta_r < convergence_epsilon:
++ break
++
++ return np.maximum(radii, 0.0) # Ensure no negative radii
++
++# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
++class HybridLocalSearcher:
++ """
++ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
++ This component refines circle center positions to maximize radii sum in a local region.
++ Combines ideas from multiple high-performing programs for robust optimization.
++ """
++ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
++ wall_strength=0.8, initial_growth_pressure=1.05,
++ final_growth_pressure=1.001, fine_tune_lr=0.001,
++ radius_sim_iter=80):
++ self.sim_iter = sim_iter
++ self.fine_tune_iter = fine_tune_iter
++ self.learning_rate = learning_rate
++ self.wall_strength = wall_strength
++ self.initial_growth_pressure = initial_growth_pressure
++ self.final_growth_pressure = final_growth_pressure
++ self.fine_tune_lr = fine_tune_lr
++ self.radius_sim_iter = radius_sim_iter
++
++ def optimize(self, centers):
++ """Refines circle centers using the force-directed simulation."""
++ # Using a copy to avoid modifying the original array if `centers` is passed by reference
++ current_centers = centers.copy()
++
++ # --- Phase 1: Annealed Growth Pressure ---
++ # This phase uses artificially inflated radii to create strong repulsive forces,
++ # helping circles to escape local minima and find more open spaces.
++ for i_iter in range(self.sim_iter):
++ progress = i_iter / self.sim_iter
++ # Growth pressure anneals from initial to final value
++ growth_pressure = self.final_growth_pressure + \
++ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
++
++ # Use faster radius calculation during simulation for performance
++ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter)
++ pressured_radii = radii * growth_pressure
++
++ forces = self._calculate_forces(current_centers, pressured_radii)
++
++ # Learning rate also anneals, starting larger for exploration and shrinking for stability
++ lr = self.learning_rate * (1.0 - progress)**2
++ current_centers += forces * lr
++ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
++
++ # --- Phase 2: Fine-Tuning ---
++ # After the aggressive exploration, this phase settles the packing without artificial pressure,
++ # allowing for precise placement based on actual overlaps.
++ for _ in range(self.fine_tune_iter):
++ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
++ forces = self._calculate_forces(current_centers, radii)
++ current_centers += forces * self.fine_tune_lr
++ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
++
++ return current_centers
++
++ def _calculate_forces(self, centers, effective_radii):
++ """
++ Calculates repulsion forces from circle overlaps and wall proximity.
++ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
++ """
++ forces = np.zeros_like(centers)
++
++ # Vectorized Circle-to-circle repulsion
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
++
++ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
++ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
++
++ force_magnitudes = overlaps # Force magnitude proportional to overlap
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
++ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
++
++ # Vectorized Wall repulsion (pushes circles away from boundaries)
++ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
++ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
++ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
++ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
++
++ return forces
++
++# --- GA Core Components ---
++class Individual:
++ """Represents a single candidate packing solution (a set of circle centers)."""
++ def __init__(self, centers_array, n_circles):
++ self.centers = np.array(centers_array).reshape(n_circles, 2)
++ self.radii = None
++ self.fitness = -1.0 # Sum of radii, initialized to a low value
++
++ def evaluate_fitness(self, radius_iter_val):
++ """Calculates the sum of radii for the current center configuration."""
++ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
++ self.fitness = np.sum(self.radii)
++ return self.fitness
++
++def initialize_population(n_circles, population_size):
++ """
++ Initializes a diverse population for the Genetic Algorithm.
++ Includes grid-based patterns (strong baselines) and purely random configurations
++ to ensure broad exploration.
++ """
++ population = []
++
++ # --- Strategy: Grid + Extra Point Variations ---
++ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+- base_25_centers = np.zeros((n - 1, 2))
++ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+- base_25_centers[k, 0] = (i + 0.5) * spacing
+- base_25_centers[k, 1] = (j + 0.5) * spacing
++ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
++ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+- # Strategic candidate placements for the 26th circle.
+- # Expanded list to explore a wider range of starting configurations.
+- candidate_extra_positions = [
+- # Interstitial points from 5x5 grid
++ # Strategic candidate positions for the 26th circle
++ extra_pos_candidates = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+- [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+- [0.5, 0.5], # (0.5, 0.5) - Center
+- [0.5, 1 - spacing], # (0.5, 0.8)
+- [1 - spacing, spacing], # (0.8, 0.2)
+- [1 - spacing, 0.5], # (0.8, 0.5)
+- [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- # Corner-offset points
+- [0.05, 0.05],
+- [0.05, 0.95],
+- [0.95, 0.05],
+- [0.95, 0.95],
+- # Edge midpoints
+- [0.05, 0.5],
+- [0.5, 0.05],
+- [0.95, 0.5],
+- [0.5, 0.95]
++ [0.5, 0.5], # Center
++ [0.05, 0.05], [0.95, 0.05], # Near corners
++ [0.05, 0.95], [0.95, 0.95],
++ [0.5, 0.1], [0.1, 0.5], # Near edge midpoints
++ [0.5, 0.9], [0.9, 0.5],
++ [0.25, 0.25], [0.75, 0.75], # Diagonal
++ [0.25, 0.75], [0.75, 0.25],
++ [0.3, 0.1], [0.7, 0.1], # more points
++ [0.1, 0.3], [0.1, 0.7]
+ ]
+-
+- best_sum_radii = -1.0
+- best_centers_overall = None
+- best_radii_overall = None
+-
+- # Add a stronger perturbation to the base grid to better break symmetry and encourage rearrangement.
+- perturbation_strength = 0.025 * spacing # 2.5% of the grid spacing
+-
+- # Iterate through each candidate position, running multiple trials for each to find a better optimum.
+- for extra_pos in candidate_extra_positions:
+- for _ in range(num_trials_per_candidate):
+- # Create a new 'centers' array for this trial
+- current_centers_trial = np.zeros((n, 2))
+-
+- # Start with a fresh copy of the base grid and perturb it
+- perturbed_grid = base_25_centers.copy()
+- perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+- current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+-
+- current_centers_trial[n-1] = np.array(extra_pos)
+-
+- # Refine these centers using the simulation, passing all necessary hyperparameters
+- refined_centers = refine_centers_with_simulation(
+- current_centers_trial,
+- sim_iter, learning_rate, wall_strength,
+- initial_growth_pressure, final_growth_pressure,
+- fine_tune_iter, fine_tune_lr, radius_sim_iter
+- )
+-
+- # Compute the final, precise radii for this refined configuration
+- current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+- current_sum_radii = np.sum(current_radii_final)
+-
+- # Update if this is the best packing found so far
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers_overall = refined_centers.copy()
+- best_radii_overall = current_radii_final.copy()
+-
+- # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+- if best_centers_overall is None:
+- # Revert to a safe default if no better solution was found
+- default_centers = np.zeros((n, 2))
+- default_centers[:n-1] = base_25_centers
+- default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+- default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+- return default_centers, default_radii
+- else:
+- return best_centers_overall, best_radii_overall
+-
+-
+-def refine_centers_with_simulation(
+- centers,
+- sim_iter, learning_rate, wall_strength,
+- initial_growth_pressure, final_growth_pressure,
+- fine_tune_iter, fine_tune_lr, radius_sim_iter
+-):
+- """
+- Refines circle centers using a two-phase force-directed simulation.
+- It first uses an annealed 'growth pressure' to explore the solution space,
+- then fine-tunes the positions with actual overlaps.
+-
+- Args:
+- centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+- sim_iter: Number of iterations for the main simulation phase.
+- learning_rate: Base learning rate for center updates.
+- wall_strength: Strength of repulsion from square walls.
+- initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+- final_growth_pressure: Final multiplier for radii.
+- fine_tune_iter: Number of iterations for the fine-tuning phase.
+- fine_tune_lr: Learning rate for the fine-tuning phase.
+- radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+-
+- Returns:
+- np.array: Refined circle centers.
+- """
+- n = centers.shape[0]
+-
+- # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+- for i_iter in range(sim_iter):
+- # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+- progress = i_iter / sim_iter
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+-
+- # 1. Calculate approximate radii for current positions
+- current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+-
+- # 2. Artificially inflate radii to create expansion 'pressure'
+- pressured_radii = current_radii * current_growth_pressure
+-
+- forces = np.zeros_like(centers)
+-
+- # 3. Calculate repulsion forces based on artificial overlaps
+- # a) Vectorized Circle-to-circle repulsion
+- diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+-
+- radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0
+- np.fill_diagonal(overlaps, 0)
+-
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b) Vectorized Wall repulsion
+- # Left wall (x=0)
+- overlap_l = pressured_radii - centers[:, 0]
+- forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+- # Right wall (x=1)
+- overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+- forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+- # Bottom wall (y=0)
+- overlap_b = pressured_radii - centers[:, 1]
+- forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+- # Top wall (y=1)
+- overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+- forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+-
+- # 4. Update positions with an annealing learning rate for stability
+- lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+- centers += forces * lr
+-
+- # 5. Enforce hard boundary constraints
+- centers = np.clip(centers, 0.0, 1.0)
+-
+- # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+- for _ in range(fine_tune_iter):
+- # 1. Calculate approximate radii based on actual positions
+- current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+-
+- forces = np.zeros_like(centers)
+-
+- # 2. Calculate repulsion forces based on *actual* overlaps
+- # a) Vectorized Circle-to-circle repulsion
+- diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9
+-
+- radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0
+- np.fill_diagonal(overlaps, 0)
+-
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b) Vectorized Wall repulsion (based on actual radii)
+- # Left wall (x=0)
+- overlap_l = current_radii - centers[:, 0]
+- forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+- # Right wall (x=1)
+- overlap_r = (centers[:, 0] + current_radii) - 1.0
+- forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+- # Bottom wall (y=0)
+- overlap_b = current_radii - centers[:, 1]
+- forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+- # Top wall (y=1)
+- overlap_t = (centers[:, 1] + current_radii) - 1.0
+- forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+-
+- # 3. Update positions with a small, constant learning rate to settle
+- centers += forces * fine_tune_lr
+-
+- # 4. Enforce hard boundary constraints
+- centers = np.clip(centers, 0.0, 1.0)
+-
+- return centers
+-
+-
+-def compute_max_radii(centers, iterations=500):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+- iterations: The number of iterations for the proportional scaling.
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling.
+- # This loop is more robust, checking for convergence based on minimal changes.
+- convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+-
+- for _ in range(iterations): # Iterate to stabilize radii
+- updated_in_pass = False
+- previous_radii = radii.copy()
+-
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- # If current radii would cause overlap and distance is not zero
+- if dist < 1e-9:
+- # If centers are identical, their radii must be zero.
+- if radii[i] > 0 or radii[j] > 0:
+- radii[i] = 0.0
+- radii[j] = 0.0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- # Scale both radii proportionally so they just touch
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- # Check for convergence: if no updates were made AND the max change is tiny.
+- max_delta_r = np.max(np.abs(radii - previous_radii))
+- if not updated_in_pass and max_delta_r < convergence_epsilon:
+- break
+-
+- return np.maximum(radii, 0) # Ensure no negative radii
+-
++
++ # Generate initial individuals from these grid configurations
++ # Aim for about 1/3 to 1/2 of population from grids
++ num_grid_starts = min(population_size // 2, len(extra_pos_candidates) * 2)
++
++ for i in range(num_grid_starts):
++ current_centers = np.zeros((n_circles, 2))
++ current_centers[:n_circles-1] = base_grid_centers_25.copy()
++
++ # Select an extra position candidate, cycle through them
++ extra_pos = extra_pos_candidates[i % len(extra_pos_candidates)]
++ current_centers[n_circles-1] = np.array(extra_pos)
++
++ # Apply a small perturbation to the grid and the extra circle
++ perturbation_strength = 0.018 # Slightly increased perturbation
++ current_centers += np.random.normal(0, perturbation_strength, (n_circles, 2))
++
++ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
++
++ # --- Strategy: Completely Random Starts ---
++ while len(population) < population_size:
++ random_centers = np.random.rand(n_circles, 2)
++ population.append(Individual(random_centers, n_circles))
++
++ return population
++
++def select_parents(population, tournament_size):
++ """Selects two distinct parents using tournament selection."""
++ # Select parent 1 by picking the fittest from a random tournament subset
++ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
++ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
++
++ # Select parent 2, ensuring it's different from parent 1
++ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
++ if len(tournament_p2_candidates) > 0:
++ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
++ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
++ parent2 = np.random.choice(population)
++ while parent2 is parent1: # Ensure parent2 is definitely different
++ parent2 = np.random.choice(population)
++ return parent1, parent2
++
++def crossover(parent1, parent2, n_circles):
++ """
++ Performs uniform crossover: for each circle, its position (x,y) is inherited
++ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
++ """
++ child_centers = np.zeros((n_circles, 2))
++ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
++ child_centers = np.where(mask, parent1.centers, parent2.centers)
++ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
++
++def mutate(individual, mutation_rate, mutation_strength, n_circles):
++ """
++ Applies Gaussian noise mutation to individual circle centers.
++ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
++ perturbed by Gaussian noise with `mutation_strength` standard deviation.
++ """
++ mutated_centers = individual.centers.copy()
++ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
++
++ # Apply Gaussian noise only to selected circles
++ if np.any(mask):
++ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
++
++ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
++ return individual
++
++# --- Main Orchestrator: Memetic Algorithm ---
++def construct_packing():
++ """
++ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
++ a Genetic Algorithm with a powerful force-directed local search.
++ This approach combines the exploration power of GA with the refinement capability
++ of local search, incorporating best practices from high-scoring prior programs.
++ """
++ n_circles = 26
++
++ # --- Hyperparameters for Memetic Algorithm ---
++ POPULATION_SIZE = 120 # Increased population size for more diversity
++ NUM_GENERATIONS = 800 # Increased generations for longer evolution
++ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
++ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
++ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
++ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
++ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
++ LOCAL_SEARCH_PROB = 0.25 # Probability to apply the powerful local search to a new child
++
++ # Radius calculation iterations: lower for speed during GA, higher for final precision
++ GA_RADIUS_ITER = 80 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
++ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
++
++ # Local Search parameters for within the GA loop (lighter, faster iterations)
++ ga_local_searcher = HybridLocalSearcher(
++ sim_iter=200, fine_tune_iter=70, learning_rate=0.02,
++ wall_strength=0.8, initial_growth_pressure=1.04,
++ final_growth_pressure=1.001, fine_tune_lr=0.001,
++ radius_sim_iter=100
++ )
++
++ # Local Search parameters for the final polish (more intensive, run once)
++ final_local_searcher = HybridLocalSearcher(
++ sim_iter=800, fine_tune_iter=250, learning_rate=0.02,
++ wall_strength=0.8, initial_growth_pressure=1.05,
++ final_growth_pressure=1.001, fine_tune_lr=0.001,
++ radius_sim_iter=200
++ )
++
++
++ # --- Initialization ---
++ population = initialize_population(n_circles, POPULATION_SIZE)
++
++ # Crucial: Initial Local Optimization of Entire Population (from Prior Program G97)
++ # Applying a light local search to all individuals in the initial population provides
++ # a strong, diverse set of locally optimized starting points for the GA.
++ for individual in population:
++ individual.centers = ga_local_searcher.optimize(individual.centers.copy())
++ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
++
++ best_individual_overall = max(population, key=lambda ind: ind.fitness)
++
++ # --- Main Genetic Algorithm Loop ---
++ for generation in range(NUM_GENERATIONS):
++ # Anneal mutation strength: decreases over time for fine-tuning
++ progress = generation / NUM_GENERATIONS
++ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
++ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
++
++ new_population = []
++
++ # Elitism: Directly carry over the best individuals to the next generation
++ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
++ new_population.extend(sorted_population[:ELITISM_COUNT])
++
++ # Generate the rest of the new population through selection, crossover, and mutation
++ while len(new_population) < POPULATION_SIZE:
++ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
++ child = crossover(parent1, parent2, n_circles)
++
++ # --- Hybrid Operator: Probabilistic Local Search ---
++ # Apply the powerful local search to a subset of children. This is a key
++ # memetic step, allowing the GA to guide global search while local search
++ # refines promising solutions efficiently.
++ if np.random.rand() < LOCAL_SEARCH_PROB:
++ child.centers = ga_local_searcher.optimize(child.centers.copy())
++
++ # Always apply a small standard mutation for ongoing diversity and exploration
++ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
++
++ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
++ new_population.append(child)
++
++ population = new_population # Replace old population with the new one
++
++ # Update the overall best individual found across all generations
++ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
++ if current_best_in_gen.fitness > best_individual_overall.fitness:
++ # Important: copy the individual to ensure we don't modify it later if it's part of the new population
++ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
++ best_individual_overall.fitness = current_best_in_gen.fitness
++ best_individual_overall.radii = current_best_in_gen.radii # Also copy radii for consistency
++
++ # Optional: print progress to monitor the search
++ # if generation % (NUM_GENERATIONS // 10) == 0:
++ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
++
++ # Final, intensive local search on the overall best individual
++ best_individual_overall.centers = final_local_searcher.optimize(best_individual_overall.centers.copy())
++ # Final high-precision evaluation
++ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
++
++ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7ec810a10c9477d6d40536c5bb1541027ca2d10
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/main.py
@@ -0,0 +1,360 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+ # Using a copy to avoid modifying the original array if `centers` is passed by reference
+ current_centers = centers.copy()
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(current_centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ current_centers += forces * lr
+ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(current_centers, radii)
+ current_centers += forces * self.fine_tune_lr
+ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
+
+ return current_centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # --- Strategy: Grid + Extra Point Variations ---
+ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
+ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate positions for the 26th circle
+ extra_pos_candidates = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # Center
+ [0.05, 0.05], [0.95, 0.05], # Near corners
+ [0.05, 0.95], [0.95, 0.95],
+ [0.5, 0.1], [0.1, 0.5], # Near edge midpoints
+ [0.5, 0.9], [0.9, 0.5],
+ [0.25, 0.25], [0.75, 0.75], # Diagonal
+ [0.25, 0.75], [0.75, 0.25],
+ [0.3, 0.1], [0.7, 0.1], # more points
+ [0.1, 0.3], [0.1, 0.7]
+ ]
+
+ # Generate initial individuals from these grid configurations
+ # Aim for about 1/3 to 1/2 of population from grids
+ num_grid_starts = min(population_size // 2, len(extra_pos_candidates) * 2)
+
+ for i in range(num_grid_starts):
+ current_centers = np.zeros((n_circles, 2))
+ current_centers[:n_circles-1] = base_grid_centers_25.copy()
+
+ # Select an extra position candidate, cycle through them
+ extra_pos = extra_pos_candidates[i % len(extra_pos_candidates)]
+ current_centers[n_circles-1] = np.array(extra_pos)
+
+ # Apply a small perturbation to the grid and the extra circle
+ perturbation_strength = 0.018 # Slightly increased perturbation
+ current_centers += np.random.normal(0, perturbation_strength, (n_circles, 2))
+
+ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
+
+ # --- Strategy: Completely Random Starts ---
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies Gaussian noise mutation to individual circle centers.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+
+ # Apply Gaussian noise only to selected circles
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 120 # Increased population size for more diversity
+ NUM_GENERATIONS = 800 # Increased generations for longer evolution
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.25 # Probability to apply the powerful local search to a new child
+
+ # Radius calculation iterations: lower for speed during GA, higher for final precision
+ GA_RADIUS_ITER = 80 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # Local Search parameters for within the GA loop (lighter, faster iterations)
+ ga_local_searcher = HybridLocalSearcher(
+ sim_iter=200, fine_tune_iter=70, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.04,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=100
+ )
+
+ # Local Search parameters for the final polish (more intensive, run once)
+ final_local_searcher = HybridLocalSearcher(
+ sim_iter=800, fine_tune_iter=250, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=200
+ )
+
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Crucial: Initial Local Optimization of Entire Population (from Prior Program G97)
+ # Applying a light local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ for individual in population:
+ individual.centers = ga_local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = ga_local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Important: copy the individual to ensure we don't modify it later if it's part of the new population
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+ best_individual_overall.radii = current_best_in_gen.radii # Also copy radii for consistency
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final, intensive local search on the overall best individual
+ best_individual_overall.centers = final_local_searcher.optimize(best_individual_overall.centers.copy())
+ # Final high-precision evaluation
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..0189b0ccbec58a48884aaafe7997037f915f638e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/original.py
@@ -0,0 +1,307 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.04 # Increased pressure to better 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+ num_trials_per_candidate = 3 # Number of simulations per candidate starting position.
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Expanded list to explore a wider range of starting configurations.
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95],
+ # Edge midpoints
+ [0.05, 0.5],
+ [0.5, 0.05],
+ [0.95, 0.5],
+ [0.5, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # Add a stronger perturbation to the base grid to better break symmetry and encourage rearrangement.
+ perturbation_strength = 0.025 * spacing # 2.5% of the grid spacing
+
+ # Iterate through each candidate position, running multiple trials for each to find a better optimum.
+ for extra_pos in candidate_extra_positions:
+ for _ in range(num_trials_per_candidate):
+ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+
+ # Start with a fresh copy of the base grid and perturb it
+ perturbed_grid = base_25_centers.copy()
+ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..014d2091a05c7295745b291c8dab008bbafd98d7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results
+Run 1/1 completed in 16124.63 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.263336784936778
+ public: {'centers_str': ' centers[0] = (0.0881, 0.0905)\n centers[1] = (0.2074, 0.0291)\n centers[2] = (0.5798, 0.0443)\n centers[3] = (0.7043, 0.0756)\n centers[4] = (0.9053, 0.0949)\n centers[5] = (0.0745, 0.2671)\n centers[6] = (0.3426, 0.6994)\n centers[7] = (0.6380, 0.5697)\n centers[8] = (0.7390, 0.2532)\n centers[9] = (0.9299, 0.3294)\n centers[10] = (0.1102, 0.4487)\n centers[11] = (0.2985, 0.5507)\n centers[12] = (0.5149, 0.5079)\n centers[13] = (0.6829, 0.4704)\n centers[14] = (0.8940, 0.5034)\n centers[15] = (0.1185, 0.6810)\n centers[16] = (0.2895, 0.7103)\n centers[17] = (0.4576, 0.6967)\n centers[18] = (0.7316, 0.7180)\n centers[19] = (0.9460, 0.6865)\n centers[20] = (0.1002, 0.9000)\n centers[21] = (0.3189, 0.8821)\n centers[22] = (0.5398, 0.8994)\n centers[23] = (0.7203, 0.9403)\n centers[24] = (0.9059, 0.9060)\n centers[25] = (0.3756, 0.2278)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.263336784936778}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/packing_viz.png
+ execution_time_mean: 16124.628194028977
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..c47eb59440f4b350aaac9b8afa60eb55d978187d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.263336784936778,
+ "public": {
+ "centers_str": " centers[0] = (0.0881, 0.0905)\n centers[1] = (0.2074, 0.0291)\n centers[2] = (0.5798, 0.0443)\n centers[3] = (0.7043, 0.0756)\n centers[4] = (0.9053, 0.0949)\n centers[5] = (0.0745, 0.2671)\n centers[6] = (0.3426, 0.6994)\n centers[7] = (0.6380, 0.5697)\n centers[8] = (0.7390, 0.2532)\n centers[9] = (0.9299, 0.3294)\n centers[10] = (0.1102, 0.4487)\n centers[11] = (0.2985, 0.5507)\n centers[12] = (0.5149, 0.5079)\n centers[13] = (0.6829, 0.4704)\n centers[14] = (0.8940, 0.5034)\n centers[15] = (0.1185, 0.6810)\n centers[16] = (0.2895, 0.7103)\n centers[17] = (0.4576, 0.6967)\n centers[18] = (0.7316, 0.7180)\n centers[19] = (0.9460, 0.6865)\n centers[20] = (0.1002, 0.9000)\n centers[21] = (0.3189, 0.8821)\n centers[22] = (0.5398, 0.8994)\n centers[23] = (0.7203, 0.9403)\n centers[24] = (0.9059, 0.9060)\n centers[25] = (0.3756, 0.2278)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.263336784936778
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/results/packing_viz.png",
+ "execution_time_mean": 16124.628194028977,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..883488beffe8346c1c9a192073651750de34adbe
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_104/rewrite.txt
@@ -0,0 +1,351 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+ # Using a copy to avoid modifying the original array if `centers` is passed by reference
+ current_centers = centers.copy()
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(current_centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ current_centers += forces * lr
+ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(current_centers, radii)
+ current_centers += forces * self.fine_tune_lr
+ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
+
+ return current_centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # --- Strategy: Grid + Extra Point Variations ---
+ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
+ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate positions for the 26th circle
+ extra_pos_candidates = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # Center
+ [0.05, 0.05], [0.95, 0.05], # Near corners
+ [0.05, 0.95], [0.95, 0.95],
+ [0.5, 0.1], [0.1, 0.5], # Near edge midpoints
+ [0.5, 0.9], [0.9, 0.5],
+ [0.25, 0.25], [0.75, 0.75], # Diagonal
+ [0.25, 0.75], [0.75, 0.25],
+ [0.3, 0.1], [0.7, 0.1], # more points
+ [0.1, 0.3], [0.1, 0.7]
+ ]
+
+ # Generate initial individuals from these grid configurations
+ # Aim for about 1/3 to 1/2 of population from grids
+ num_grid_starts = min(population_size // 2, len(extra_pos_candidates) * 2)
+
+ for i in range(num_grid_starts):
+ current_centers = np.zeros((n_circles, 2))
+ current_centers[:n_circles-1] = base_grid_centers_25.copy()
+
+ # Select an extra position candidate, cycle through them
+ extra_pos = extra_pos_candidates[i % len(extra_pos_candidates)]
+ current_centers[n_circles-1] = np.array(extra_pos)
+
+ # Apply a small perturbation to the grid and the extra circle
+ perturbation_strength = 0.018 # Slightly increased perturbation
+ current_centers += np.random.normal(0, perturbation_strength, (n_circles, 2))
+
+ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
+
+ # --- Strategy: Completely Random Starts ---
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies Gaussian noise mutation to individual circle centers.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+
+ # Apply Gaussian noise only to selected circles
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 120 # Increased population size for more diversity
+ NUM_GENERATIONS = 800 # Increased generations for longer evolution
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.25 # Probability to apply the powerful local search to a new child
+
+ # Radius calculation iterations: lower for speed during GA, higher for final precision
+ GA_RADIUS_ITER = 80 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # Local Search parameters for within the GA loop (lighter, faster iterations)
+ ga_local_searcher = HybridLocalSearcher(
+ sim_iter=200, fine_tune_iter=70, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.04,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=100
+ )
+
+ # Local Search parameters for the final polish (more intensive, run once)
+ final_local_searcher = HybridLocalSearcher(
+ sim_iter=800, fine_tune_iter=250, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=200
+ )
+
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Crucial: Initial Local Optimization of Entire Population (from Prior Program G97)
+ # Applying a light local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ for individual in population:
+ individual.centers = ga_local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = ga_local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Important: copy the individual to ensure we don't modify it later if it's part of the new population
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+ best_individual_overall.radii = current_best_in_gen.radii # Also copy radii for consistency
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final, intensive local search on the overall best individual
+ best_individual_overall.centers = final_local_searcher.optimize(best_individual_overall.centers.copy())
+ # Final high-precision evaluation
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..dbbfa4967c4885ef87ec9034dcad4c42a9b9efba
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/edit.diff
@@ -0,0 +1,271 @@
+--- a/original.py
++++ b/original.py
+@@ -1,248 +1,256 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+-def hybrid_local_searcher(centers, sim_iter=120, fine_tune_iter=30, radius_sim_iter=35):
++def hybrid_local_searcher(centers, sim_iter=200, fine_tune_iter=70, radius_sim_iter=70):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+- # Tuned hyperparameters for fast but effective local optimization
++ # Tuned hyperparameters for robust local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+ class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+ def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+ def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+ def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+ def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+- POPULATION_SIZE = 80
+- NUM_GENERATIONS = 300
++ POPULATION_SIZE = 100 # Increased population for broader search
++ NUM_GENERATIONS = 600 # Increased generations due to faster evaluations
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+- TOURNAMENT_SIZE = 5
+- ELITISM_COUNT = 4
+- LOCAL_SEARCH_PROB = 0.7 # Probability of applying local search to a new child
+-
+- FINAL_RADIUS_ITER = 500
++ TOURNAMENT_SIZE = 7 # Increased tournament size for stronger selection pressure
++ ELITISM_COUNT = 6 # Increased elite count to preserve more good solutions
++ LOCAL_SEARCH_PROB = 0.25 # Reduced probability for local search to balance exploration/exploitation
++
++ GA_RADIUS_ITER = 70 # Faster radius evaluation for GA loop
++ FINAL_RADIUS_ITER = 500 # High-precision for final best solution
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+- individual.evaluate_fitness(FINAL_RADIUS_ITER)
++ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+- child.evaluate_fitness(FINAL_RADIUS_ITER)
++ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+- best_individual_overall = current_best_in_gen
++ # Create a new Individual object to ensure the best solution's state is preserved
++ # and not modified by subsequent GA operations if it happens to be an elite.
++ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
++ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
++ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
++
++ # Final evaluation of the best individual with high precision
++ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..6868f07628cb6a6d9381e1a412c20756dd87bda8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/main.py
@@ -0,0 +1,256 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=200, fine_tune_iter=70, radius_sim_iter=70):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for robust local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 100 # Increased population for broader search
+ NUM_GENERATIONS = 600 # Increased generations due to faster evaluations
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7 # Increased tournament size for stronger selection pressure
+ ELITISM_COUNT = 6 # Increased elite count to preserve more good solutions
+ LOCAL_SEARCH_PROB = 0.25 # Reduced probability for local search to balance exploration/exploitation
+
+ GA_RADIUS_ITER = 70 # Faster radius evaluation for GA loop
+ FINAL_RADIUS_ITER = 500 # High-precision for final best solution
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+ # Final evaluation of the best individual with high precision
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..f43e03b099c1fc4d99709630376f0aab6b5dad92
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/original.py
@@ -0,0 +1,248 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=120, fine_tune_iter=30, radius_sim_iter=35):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 300
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 5
+ ELITISM_COUNT = 4
+ LOCAL_SEARCH_PROB = 0.7 # Probability of applying local search to a new child
+
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(FINAL_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..417bf5ee8ce6b6666ce71a8b7acd9d154e78f35e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_105/search_replace.txt
@@ -0,0 +1,156 @@
+
+
+The previous program significantly reduced the performance of the `hybrid_local_searcher` by using very low iteration counts (`sim_iter=120`, `fine_tune_iter=30`, `radius_sim_iter=35`) compared to earlier successful versions (e.g., G97's `sim_iter=300`, `fine_tune_iter=100`, `radius_sim_iter=80`). While reduced iterations can speed up the process, these values appear too low for effective local optimization.
+
+Furthermore, a critical issue was identified: the `construct_packing` function was using `FINAL_RADIUS_ITER` (500 iterations) for *all* fitness evaluations within the Genetic Algorithm loop, instead of a faster `GA_RADIUS_ITER`. This makes each evaluation very slow, hindering the GA's ability to explore effectively due to the limited number of generations and population size it could run within a reasonable time.
+
+This edit addresses these issues by:
+1. **Increasing `hybrid_local_searcher`'s iteration counts:** `sim_iter` to 200, `fine_tune_iter` to 70, and `radius_sim_iter` to 70. These values strike a better balance between computational cost and effectiveness for the frequently called local search.
+2. **Reintroducing `GA_RADIUS_ITER`:** A specific `GA_RADIUS_ITER` (set to 70, matching `radius_sim_iter`) is now used for fitness evaluations inside the GA loop, drastically speeding up the genetic evolution process. `FINAL_RADIUS_ITER` is retained only for the final, high-precision evaluation of the best solution.
+
+
+<<<<<<< SEARCH
+def hybrid_local_searcher(centers, sim_iter=120, fine_tune_iter=30, radius_sim_iter=35):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+=======
+def hybrid_local_searcher(centers, sim_iter=200, fine_tune_iter=70, radius_sim_iter=70):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for robust local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+>>>>>>> REPLACE
+<<<<<<< SEARCH
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 300
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 5
+ ELITISM_COUNT = 4
+ LOCAL_SEARCH_PROB = 0.7 # Probability of applying local search to a new child
+
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(FINAL_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+=======
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 100 # Increased population for broader search
+ NUM_GENERATIONS = 600 # Increased generations due to faster evaluations
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7 # Increased tournament size for stronger selection pressure
+ ELITISM_COUNT = 6 # Increased elite count to preserve more good solutions
+ LOCAL_SEARCH_PROB = 0.25 # Reduced probability for local search to balance exploration/exploitation
+
+ GA_RADIUS_ITER = 70 # Faster radius evaluation for GA loop
+ FINAL_RADIUS_ITER = 500 # High-precision for final best solution
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+ # Final evaluation of the best individual with high precision
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..b46e4786837463e2e0c8a4e09593b6e954dde127
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/edit.diff
@@ -0,0 +1,235 @@
+--- a/original.py
++++ b/original.py
+@@ -1,216 +1,222 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+ import math
+ import random
+
+ def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+- 'num_starts': 12, # Number of independent SA runs
+- 't_start': 0.05, # Initial temperature
++ 'num_starts': 16, # More starts to explore different basins
++ 't_start': 0.06, # Higher initial temperature for more exploration
+ 't_end': 1e-6, # Final temperature
+- 'cooling_rate': 0.998, # Geometric cooling factor
+- 'moves_per_temp': 60, # Number of moves at each temperature step
+- 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
++ 'cooling_rate': 0.999, # Slower cooling for more thorough search
++ 'moves_per_temp': 50, # Adjusted moves to balance longer schedule
++ 'max_step_size': 0.20, # Larger initial step size
+ 'radius_iter': 250, # Radius calculation iterations during search
+- 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+- 'cluster_size': 4,
++ 'move_weights': [0.70, 0.20, 0.10], # Increased probability for powerful swap move
++ 'cluster_size': 5, # Slightly larger clusters
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+- if i < config['num_starts'] // 2:
++ # Prioritize structured starts (12 of 16), which are generally more promising
++ if i < 12:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+- # Place the 26th circle in a strategic corner or center
+- extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
++ # Use a richer set of candidate positions for the 26th circle
++ extra_pos_candidates = [
++ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
++ [0.5, 0.5], # Center
++ [0.2, 0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8], # Grid interstitial (spacing=0.2)
++ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
++ ]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fa5a9ebe7d113d7114155bce9bb2ac44a405dc4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/main.py
@@ -0,0 +1,222 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 16, # More starts to explore different basins
+ 't_start': 0.06, # Higher initial temperature for more exploration
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.999, # Slower cooling for more thorough search
+ 'moves_per_temp': 50, # Adjusted moves to balance longer schedule
+ 'max_step_size': 0.20, # Larger initial step size
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.70, 0.20, 0.10], # Increased probability for powerful swap move
+ 'cluster_size': 5, # Slightly larger clusters
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ # Prioritize structured starts (12 of 16), which are generally more promising
+ if i < 12:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Use a richer set of candidate positions for the 26th circle
+ extra_pos_candidates = [
+ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
+ [0.5, 0.5], # Center
+ [0.2, 0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8], # Grid interstitial (spacing=0.2)
+ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
+ ]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ff95302fa28431aa45cfdecfc5bf767e17899a4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/original.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 12, # Number of independent SA runs
+ 't_start': 0.05, # Initial temperature
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.998, # Geometric cooling factor
+ 'moves_per_temp': 60, # Number of moves at each temperature step
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+ 'cluster_size': 4,
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Place the 26th circle in a strategic corner or center
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0f58c204ec0c8044bfe4f5d9f08af36cbc08e459
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/search_replace.txt
@@ -0,0 +1,93 @@
+Based on my analysis of the current simulated annealing approach and the problem's geometry, I propose two key improvements. First, I'll refine the annealing schedule and move set to encourage a longer, more thorough, and globally-aware search. Second, I will diversify the initial configurations by increasing the number of structured starts and expanding the candidate positions for the 26th circle, drawing inspiration from previously successful Memetic Algorithms. These changes aim to better explore the search space and increase the probability of discovering a superior packing configuration.
+
+
+refined_annealing_schedule
+
+
+This change adjusts the simulated annealing hyperparameters to promote a more extensive and effective search.
+- **`num_starts`** is increased from 12 to 16 to explore more regions of the solution space.
+- **`t_start`** is raised to 0.06 and **`max_step_size`** to 0.20 to facilitate more aggressive exploration at the beginning of the search, helping to break out of initial local optima.
+- **`cooling_rate`** is increased to 0.999 for a slower, more patient annealing process, allowing the system more time to settle into better configurations at each temperature.
+- **`moves_per_temp`** is slightly reduced to balance the substantially longer schedule.
+- **`move_weights`** are adjusted to `[0.70, 0.20, 0.10]`, doubling the probability of the powerful global `swap` move, which can be crucial for rearranging trapped circles.
+- **`cluster_size`** is increased to 5 to allow for slightly larger correlated movements.
+Collectively, these changes create a more robust annealing process capable of finding higher-quality solutions.
+
+
+<<<<<<< SEARCH
+ config = {
+ 'num_starts': 12, # Number of independent SA runs
+ 't_start': 0.05, # Initial temperature
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.998, # Geometric cooling factor
+ 'moves_per_temp': 60, # Number of moves at each temperature step
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+ 'cluster_size': 4,
+ }
+=======
+ config = {
+ 'num_starts': 16, # More starts to explore different basins
+ 't_start': 0.06, # Higher initial temperature for more exploration
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.999, # Slower cooling for more thorough search
+ 'moves_per_temp': 50, # Adjusted moves to balance longer schedule
+ 'max_step_size': 0.20, # Larger initial step size
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.70, 0.20, 0.10], # Increased probability for powerful swap move
+ 'cluster_size': 5, # Slightly larger clusters
+ }
+>>>>>>> REPLACE
+
+
+diversify_initial_configurations
+
+
+This edit enhances the multi-start strategy by prioritizing structured initializations and increasing their diversity. The number of grid-based starts is increased from 6 to 12 (out of 16 total starts), as these are more likely to be in a promising region of the search space than purely random configurations. Furthermore, the list of candidate positions for the 26th circle is significantly expanded. By including more strategic points—such as grid interstitial points and offset edge centers—we provide the annealer with a richer and more varied set of high-quality starting configurations, improving the chances of one of the runs converging to a global optimum.
+
+
+<<<<<<< SEARCH
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Place the 26th circle in a strategic corner or center
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+=======
+ # Prioritize structured starts (12 of 16), which are generally more promising
+ if i < 12:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Use a richer set of candidate positions for the 26th circle
+ extra_pos_candidates = [
+ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
+ [0.5, 0.5], # Center
+ [0.2, 0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8], # Grid interstitial (spacing=0.2)
+ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
+ ]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..108429a2009edfa8c87691be1766fb256abea820
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..848411dba6b657a4f6349b58bcdf35a0194f97e6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/edit.diff
@@ -0,0 +1,336 @@
+--- a/original.py
++++ b/original.py
+@@ -1,283 +1,288 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+-class LocalSearchRefiner:
+- """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+- def __init__(self, config):
++class HybridLocalSearcher:
++ """
++ A powerful local search refiner based on a two-phase physical simulation.
++ This approach incorporates a "growth pressure" concept for exploration.
++ """
++ def __init__(self, n, config):
++ self.n = n
+ self.config = config
+
++ def _compute_radii_for_sim(self, centers):
++ """A fast radius computation for use inside the simulation loop."""
++ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
++
+ def refine(self, centers):
+- """Applies a two-stage force-directed refinement to polish a solution."""
++ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+- # --- Stage 1: Aggressive settling with subtle growth pressure ---
+- for i_agg_iter in range(self.config['ls_aggressive_iter']):
+- progress = i_agg_iter / self.config['ls_aggressive_iter']
+- current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+- (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+- (1.0 - progress)**1.5 # Cubic decay for growth pressure
+-
+- radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+- pressured_radii = radii * current_growth_pressure
+-
+- # Vectorized Force Calculation
++ # Phase 1: Main refinement with decreasing growth pressure
++ iterations = self.config['sim_iter']
++ base_lr = self.config['learning_rate']
++ wall_strength = self.config['wall_strength']
++ initial_growth_pressure = self.config['initial_growth_pressure']
++ final_growth_pressure = self.config['final_growth_pressure']
++
++ for i_iter in range(iterations):
++ progress = i_iter / iterations
++ # Anneal growth pressure quadratically
++ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++ current_radii = self._compute_radii_for_sim(refined_centers)
++ pressured_radii = current_radii * current_growth_pressure
++
++ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+-
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+-
+- force_mags = overlaps
+- force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+- wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+- wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
++ # Wall forces calculation
++ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+-
+- current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+- (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+- (1.0 - progress)**2.0 # Quadratic decay for LR
+-
+- refined_centers += forces * current_lr_agg
++ # Anneal learning rate quadratically
++ lr = base_lr * (1.0 - progress)**2
++ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+- # --- Stage 2: Fine-tuning without growth pressure ---
+- for _ in range(self.config['ls_fine_tune_iter']):
+- radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+-
+- # Vectorized Force Calculation (no growth pressure)
++ # Phase 2: Fine-tuning with minimal growth pressure
++ iterations_ft = self.config['fine_tune_iter']
++ lr_ft = self.config['fine_tune_lr']
++ for _ in range(iterations_ft):
++ current_radii = self._compute_radii_for_sim(refined_centers)
++
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+-
+- radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
++ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+-
+- force_mags = overlaps
+- force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+- wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+- wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
++ # Wall forces calculation
++ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+-
+- refined_centers += forces * self.config['ls_fine_tune_lr']
++ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+ class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+
+ current_mutation_rate = self.config['mutation_rate_end'] + \
+ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 400, # Increased generations for more thorough evolution
+ 'elite_count': 4,
+ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+
+ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+
+ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ba9b6065f80031a757d3fcc8ec238d8988a4c68
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/main.py
@@ -0,0 +1,288 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This approach incorporates a "growth pressure" concept for exploration.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ # Wall forces calculation
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ # Anneal learning rate quadratically
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ # Wall forces calculation
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+
+ current_mutation_rate = self.config['mutation_rate_end'] + \
+ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 400, # Increased generations for more thorough evolution
+ 'elite_count': 4,
+ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+
+ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+
+ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..e53e50b4f5ba23507c3bd0b593eab9b77ba8edad
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/original.py
@@ -0,0 +1,283 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+
+ # --- Stage 1: Aggressive settling with subtle growth pressure ---
+ for i_agg_iter in range(self.config['ls_aggressive_iter']):
+ progress = i_agg_iter / self.config['ls_aggressive_iter']
+ current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+ (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+ (1.0 - progress)**1.5 # Cubic decay for growth pressure
+
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+ pressured_radii = radii * current_growth_pressure
+
+ # Vectorized Force Calculation
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+ (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+ (1.0 - progress)**2.0 # Quadratic decay for LR
+
+ refined_centers += forces * current_lr_agg
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning without growth pressure ---
+ for _ in range(self.config['ls_fine_tune_iter']):
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+
+ # Vectorized Force Calculation (no growth pressure)
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.config['ls_fine_tune_lr']
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+
+ current_mutation_rate = self.config['mutation_rate_end'] + \
+ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 400, # Increased generations for more thorough evolution
+ 'elite_count': 4,
+ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+
+ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+
+ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..8ffcde619295aa8930f67094c160026a5ea79071
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": false,
+ "error": "name 'LocalSearchRefiner' is not defined"
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..2439848493ba589ad63a4653a64a8696ef0c1f39
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/job_log.out
@@ -0,0 +1,14 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results
+Evaluation error: name 'LocalSearchRefiner' is not defined
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/metrics.json
+Evaluation or Validation failed: name 'LocalSearchRefiner' is not defined
+Metrics:
+ combined_score: 0.0
+ execution_time_mean: 0.0
+ execution_time_std: 0.0
+ num_successful_runs: 0
+ num_valid_runs: 0
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..fec2021a6a036637c9d3049ae9778c77cef78421
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/results/metrics.json
@@ -0,0 +1,9 @@
+{
+ "combined_score": 0.0,
+ "execution_time_mean": 0.0,
+ "execution_time_std": 0.0,
+ "num_successful_runs": 0,
+ "num_valid_runs": 0,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f163dfa3cee14d989cdf94f29410a2bad3e909bc
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_107/search_replace.txt
@@ -0,0 +1,197 @@
+My analysis indicates that the current algorithm's performance (score: 2.27) is a significant regression from previous high-scoring versions. The primary reasons for this decline are:
+1. **Ineffective Crossover:** The `Blend Crossover` averages parent coordinates, which is destructive for circle packing as it fails to preserve well-packed sub-structures (building blocks). Reverting to `Uniform Crossover` (swapping entire circles) is crucial.
+2. **Weak Local Search:** The current `LocalSearchRefiner` uses very subtle "growth pressure" and few iterations. This is insufficient for aggressively exploring the solution space. Restoring the more powerful `HybridLocalSearcher` with its two-phase simulation and higher growth pressure is necessary for deep refinement.
+3. **Poor Initialization:** The population initialization is weak, using only a quarter of the population from a grid and sampling only four starting points for the 26th circle. This starves the algorithm of diverse, high-quality starting points.
+
+To fix this, I propose a coordinated overhaul that restores the proven high-performance architecture from a previous successful implementation. These changes replace the weakened components with their more effective counterparts and re-tune the entire configuration to support this superior algorithm structure.
+
+
+restore_high_performance_architecture
+
+
+This set of edits performs a comprehensive restoration of the high-performance Memetic Algorithm structure, addressing the key regressions in the current version.
+
+1. **Restore Powerful Local Search:** The weak `LocalSearchRefiner` is replaced with the `HybridLocalSearcher` from a prior high-scoring version. This reintroduces the critical two-phase simulation with significant "growth pressure," which is essential for aggressively refining solutions and escaping local minima.
+
+2. **Restore Effective Genetic Operators and Strategic Initialization:** The `MemeticAlgorithm` class is updated to use `Uniform Crossover` instead of the destructive `Blend Crossover`, preserving valuable sub-packings. The population initialization is vastly improved to generate half the population from perturbed grids, using a much larger and more diverse set of 17 strategic starting points for the 26th circle. A bug in the parent selection (`self.select_parent()` instead of `self._select_parent()`) is also fixed.
+
+3. **Overhaul and Tune Configuration:** The `construct_packing` function is updated with a completely new configuration dictionary. This config is tailored to the restored high-performance components, including a nested `ls_config` for the `HybridLocalSearcher`, parameters for the strategic initialization (`initial_candidates`), a higher probability for local search, and better-tuned MA parameters (elite count, tournament size, mutation rates).
+
+
+<<<<<<< SEARCH
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+
+ # --- Stage 1: Aggressive settling with subtle growth pressure ---
+ for i_agg_iter in range(self.config['ls_aggressive_iter']):
+ progress = i_agg_iter / self.config['ls_aggressive_iter']
+ current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+ (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+ (1.0 - progress)**1.5 # Cubic decay for growth pressure
+
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+ pressured_radii = radii * current_growth_pressure
+
+ # Vectorized Force Calculation
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+ (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+ (1.0 - progress)**2.0 # Quadratic decay for LR
+
+ refined_centers += forces * current_lr_agg
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning without growth pressure ---
+ for _ in range(self.config['ls_fine_tune_iter']):
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+
+ # Vectorized Force Calculation (no growth pressure)
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.config['ls_fine_tune_lr']
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+=======
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This approach incorporates a "growth pressure" concept for exploration.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ # Wall forces calculation
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ # Anneal learning rate quadratically
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ # Wall forces calculation
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..720ceaa8a14139b7e293e9dddd8f492160d75c41
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/edit.diff
@@ -0,0 +1,400 @@
+--- a/original.py
++++ b/original.py
+@@ -1,332 +1,363 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ # --- Core Utility: Radius Calculation ---
+-# Adopted from constructor; more robust with convergence check.
+ def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+-
++
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+-
++
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+ # --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+ class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+- def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+- wall_strength=0.8, initial_growth_pressure=1.05,
+- final_growth_pressure=1.001, fine_tune_lr=0.001,
+- radius_sim_iter=80):
++ def __init__(self, sim_iter=400, fine_tune_iter=150, learning_rate=0.02,
++ wall_strength=0.8, initial_growth_pressure=1.05,
++ final_growth_pressure=1.001, fine_tune_lr=0.001,
++ radius_sim_iter=100):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+-
++
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+-
++
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+-
++
+ forces = self._calculate_forces(centers, pressured_radii)
+-
++
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+-
++
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+-
++
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+-
++
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+-
++
+ return forces
+
+ # --- GA Core Components ---
+ class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+ def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+-
++
+ # 1. Strong baseline from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+- break
++ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles: # For n=26, this places the 26th circle at index 25
+ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+
+ # Add the base grid configuration
+ population.append(Individual(grid_centers_base.copy(), n_circles))
+
+ # Add several perturbed versions of the grid for more diverse starting points
+- for _ in range(population_size // 4): # Approximately 25% of the population
+- perturbed_centers = grid_centers_base + np.random.normal(0, 0.015, (n_circles, 2)) # Increased perturbation strength
++ perturbed_grid_count = population_size // 5
++ perturbation_strength_grid = 0.02 # A balanced perturbation strength
++ for _ in range(perturbed_grid_count):
++ perturbed_centers = grid_centers_base + np.random.normal(0, perturbation_strength_grid, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+- # 2. Fill the rest of the population with purely random configurations for broad diversity
++ # 2. Add configurations based on a 6x5 grid structure for additional topological diversity
++ # (Crossover inspiration from prior program's diverse initializations)
++ if n_circles <= 30: # Only if it fits a 6x5 grid or less
++ num_6x5_configs = population_size // 10 # Add a few of these
++ nx, ny = 6, 5
++ spacing_x = 1.0 / nx
++ spacing_y = 1.0 / ny
++ grid_6x5_base = np.zeros((nx * ny, 2))
++ k_6x5 = 0
++ for j_6x5 in range(ny):
++ for i_6x5 in range(nx):
++ grid_6x5_base[k_6x5, 0] = (i_6x5 + 0.5) * spacing_x
++ grid_6x5_base[k_6x5, 1] = (j_6x5 + 0.5) * spacing_y
++ k_6x5 += 1
++
++ for _ in range(num_6x5_configs):
++ # Take the first n_circles from the 6x5 grid and perturb them
++ config_6x5 = grid_6x5_base[:n_circles].copy()
++ config_6x5 += np.random.normal(0, 0.02, (n_circles, 2)) # Perturb slightly
++ population.append(Individual(np.clip(config_6x5, 0.0, 1.0), n_circles))
++
++
++ # 3. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+-
++
+ return population
+
+ def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+-
++
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+ def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+ def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter (always applied) ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ # Swap the centers
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+ # --- Main Orchestrator: Memetic Algorithm ---
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+- POPULATION_SIZE = 100 # Number of candidate solutions in each generation
+- NUM_GENERATIONS = 750 # Total number of evolutionary steps
+- MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+- INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
++ POPULATION_SIZE = 120 # Increased population for broader search
++ NUM_GENERATIONS = 800 # Total number of evolutionary steps
++ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
++ INITIAL_MUTATION_STRENGTH = 0.08 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+- TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
++ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+- LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
+-
++ LOCAL_SEARCH_PROB = 0.20 # Probability to apply the powerful local search to a new child
++
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+- # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
++ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3 and Current) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+-
++
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+-
++
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+-
++
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
++ # It's important to create a deep copy if Individual is mutable and
++ # we don't want it to change in subsequent generations.
++ # However, the GA's operations usually create new Individual objects.
++ # If the best individual is part of the elite, it might be modified.
++ # To be safe, ensure deep copy for best_individual_overall if centers can be changed by reference.
++ # The current approach (assigning current_best_in_gen by reference) is fine if
++ # child centers are always newly created or copied before modification.
++ # Given `child.centers = local_searcher.optimize(child.centers.copy())` and `mutate` operating on copies, this should be fine.
+ best_individual_overall = current_best_in_gen
+-
++
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..72cd98d9c5041244bd93859eb99b8d689e606067
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/main.py
@@ -0,0 +1,363 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=400, fine_tune_iter=150, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=100):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles: # For n=26, this places the 26th circle at index 25
+ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+
+ # Add the base grid configuration
+ population.append(Individual(grid_centers_base.copy(), n_circles))
+
+ # Add several perturbed versions of the grid for more diverse starting points
+ perturbed_grid_count = population_size // 5
+ perturbation_strength_grid = 0.02 # A balanced perturbation strength
+ for _ in range(perturbed_grid_count):
+ perturbed_centers = grid_centers_base + np.random.normal(0, perturbation_strength_grid, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Add configurations based on a 6x5 grid structure for additional topological diversity
+ # (Crossover inspiration from prior program's diverse initializations)
+ if n_circles <= 30: # Only if it fits a 6x5 grid or less
+ num_6x5_configs = population_size // 10 # Add a few of these
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5_base = np.zeros((nx * ny, 2))
+ k_6x5 = 0
+ for j_6x5 in range(ny):
+ for i_6x5 in range(nx):
+ grid_6x5_base[k_6x5, 0] = (i_6x5 + 0.5) * spacing_x
+ grid_6x5_base[k_6x5, 1] = (j_6x5 + 0.5) * spacing_y
+ k_6x5 += 1
+
+ for _ in range(num_6x5_configs):
+ # Take the first n_circles from the 6x5 grid and perturb them
+ config_6x5 = grid_6x5_base[:n_circles].copy()
+ config_6x5 += np.random.normal(0, 0.02, (n_circles, 2)) # Perturb slightly
+ population.append(Individual(np.clip(config_6x5, 0.0, 1.0), n_circles))
+
+
+ # 3. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter (always applied) ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ # Swap the centers
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 120 # Increased population for broader search
+ NUM_GENERATIONS = 800 # Total number of evolutionary steps
+ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.08 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.20 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3 and Current) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # It's important to create a deep copy if Individual is mutable and
+ # we don't want it to change in subsequent generations.
+ # However, the GA's operations usually create new Individual objects.
+ # If the best individual is part of the elite, it might be modified.
+ # To be safe, ensure deep copy for best_individual_overall if centers can be changed by reference.
+ # The current approach (assigning current_best_in_gen by reference) is fine if
+ # child centers are always newly created or copied before modification.
+ # Given `child.centers = local_searcher.optimize(child.centers.copy())` and `mutate` operating on copies, this should be fine.
+ best_individual_overall = current_best_in_gen
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..856f3ad15a0ff6cd03c98767a690e673bc6792e1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/original.py
@@ -0,0 +1,332 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles: # For n=26, this places the 26th circle at index 25
+ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+
+ # Add the base grid configuration
+ population.append(Individual(grid_centers_base.copy(), n_circles))
+
+ # Add several perturbed versions of the grid for more diverse starting points
+ for _ in range(population_size // 4): # Approximately 25% of the population
+ perturbed_centers = grid_centers_base + np.random.normal(0, 0.015, (n_circles, 2)) # Increased perturbation strength
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter (always applied) ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ # Swap the centers
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 100 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 750 # Total number of evolutionary steps
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c3af8aa70a72f514e67bcd9deccdb713e6ca7a09
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/rewrite.txt
@@ -0,0 +1,354 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=400, fine_tune_iter=150, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=100):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles: # For n=26, this places the 26th circle at index 25
+ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+
+ # Add the base grid configuration
+ population.append(Individual(grid_centers_base.copy(), n_circles))
+
+ # Add several perturbed versions of the grid for more diverse starting points
+ perturbed_grid_count = population_size // 5
+ perturbation_strength_grid = 0.02 # A balanced perturbation strength
+ for _ in range(perturbed_grid_count):
+ perturbed_centers = grid_centers_base + np.random.normal(0, perturbation_strength_grid, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Add configurations based on a 6x5 grid structure for additional topological diversity
+ # (Crossover inspiration from prior program's diverse initializations)
+ if n_circles <= 30: # Only if it fits a 6x5 grid or less
+ num_6x5_configs = population_size // 10 # Add a few of these
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5_base = np.zeros((nx * ny, 2))
+ k_6x5 = 0
+ for j_6x5 in range(ny):
+ for i_6x5 in range(nx):
+ grid_6x5_base[k_6x5, 0] = (i_6x5 + 0.5) * spacing_x
+ grid_6x5_base[k_6x5, 1] = (j_6x5 + 0.5) * spacing_y
+ k_6x5 += 1
+
+ for _ in range(num_6x5_configs):
+ # Take the first n_circles from the 6x5 grid and perturb them
+ config_6x5 = grid_6x5_base[:n_circles].copy()
+ config_6x5 += np.random.normal(0, 0.02, (n_circles, 2)) # Perturb slightly
+ population.append(Individual(np.clip(config_6x5, 0.0, 1.0), n_circles))
+
+
+ # 3. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter (always applied) ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ # Swap the centers
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 120 # Increased population for broader search
+ NUM_GENERATIONS = 800 # Total number of evolutionary steps
+ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.08 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.20 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3 and Current) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # It's important to create a deep copy if Individual is mutable and
+ # we don't want it to change in subsequent generations.
+ # However, the GA's operations usually create new Individual objects.
+ # If the best individual is part of the elite, it might be modified.
+ # To be safe, ensure deep copy for best_individual_overall if centers can be changed by reference.
+ # The current approach (assigning current_best_in_gen by reference) is fine if
+ # child centers are always newly created or copied before modification.
+ # Given `child.centers = local_searcher.optimize(child.centers.copy())` and `mutate` operating on copies, this should be fine.
+ best_individual_overall = current_best_in_gen
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e3704ace8f2579f4c0733e4fc0dde08348d14366
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_108/search_replace.txt
@@ -0,0 +1,60 @@
+
+A multi-strategy mutation operator is introduced to improve the exploration capabilities of the Genetic Algorithm. In addition to the existing Gaussian noise perturbation, two new mutation types are added:
+1. **Swap Mutation:** With a 10% probability, two randomly chosen circle centers are swapped. This can introduce significant topological changes, helping the algorithm escape local optima by reconfiguring the relative positions of larger groups of circles.
+2. **Re-initialize Mutation:** With a 5% probability, a single random circle's position is completely re-initialized to a new random location within the unit square. This is a more drastic move that allows the algorithm to introduce entirely new configurations for individual circles, preventing premature convergence to suboptimal patterns.
+These additional mutation strategies enhance the diversity of the population and enable the GA to explore the solution space more effectively, especially in the context of complex packing problems where many local optima exist.
+
+
+
+
+<<<<<<< SEARCH
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies Gaussian noise mutation to individual circle centers.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+
+ # Apply Gaussian noise only to selected circles
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+=======
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter ---
+ # Apply Gaussian noise to selected circles based on mutation_rate
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ # Randomly swap positions of two circles to create larger structural changes
+ SWAP_MUTATION_PROB = 0.10 # 10% chance for a swap mutation to occur
+ if np.random.rand() < SWAP_MUTATION_PROB:
+ if n_circles > 1: # Ensure there are at least two circles to swap
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ # Randomly re-initialize the position of one circle to explore completely new areas
+ REINIT_MUTATION_PROB = 0.05 # 5% chance for a re-initialize mutation to occur
+ if np.random.rand() < REINIT_MUTATION_PROB:
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep all centers within bounds
+ return individual
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d34722562854ec59fc65740e303b832896ef5c03
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..2b3c4da5cf6e2afd107381ab7897a0e0b833ef60
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/edit.diff
@@ -0,0 +1,350 @@
+--- a/original.py
++++ b/original.py
+@@ -1,216 +1,188 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+-import math
+-import random
++from scipy.optimize import minimize, NonlinearConstraint
+
+-def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
++def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+- Compute maximum radii using iterative proportional scaling.
+- This version is optimized for speed during search, with a final high-precision call.
++ Compute maximum radii using iterative proportional scaling with a convergence check.
++ This function is used for robust initial radius estimation and final evaluation,
++ but not directly within the core NLP optimization loop.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
++ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
++
++ # Vectorized calculation for distances to improve performance
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++
++ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+- dist_sq = np.sum((centers[i] - centers[j])**2)
+- dist = np.sqrt(dist_sq)
++ dist = dists[i, j] # Pre-calculated distance
+
++ # If circles overlap
+ if radii[i] + radii[j] > dist:
+- if dist < 1e-9:
+- radii[i], radii[j] = 0.0, 0.0
++ if dist < 1e-9: # Handle co-located centers: radii must be zero
++ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
++ radii[i], radii[j] = 0.0, 0.0
++ updated = True
+ else:
++ # Scale down radii proportionally to resolve overlap
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+- updated = True
++ updated = True
+
++ # Check for convergence: if no updates were made AND the max change is tiny.
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+- return np.maximum(radii, 0)
++ return np.maximum(radii, 0) # Ensure no negative radii (safeguard)
+
+
+-class SimulatedAnnealer:
++def objective_function(z, n_circles):
+ """
+- Performs a search for an optimal circle packing using Simulated Annealing.
++ Objective function for SciPy minimize. We want to maximize the sum of radii,
++ so we minimize the negative sum of radii.
++ z is a 1D array: [x1..xn, y1..yn, r1..rn]
+ """
+- def __init__(self, n, initial_centers, config):
+- self.n = n
+- self.config = config
+- self.centers = initial_centers
+-
+- self.temp = config['t_start']
+- self.max_step_size = config['max_step_size']
+-
+- self.energy = self._calculate_energy(self.centers)
+- self.best_centers = self.centers.copy()
+- self.best_energy = self.energy
+-
+- self.move_weights = config['move_weights']
+- self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
++ radii = z[2 * n_circles : 3 * n_circles]
++ return -np.sum(radii)
+
+- def _calculate_energy(self, centers):
+- """Energy is the negative sum of radii, to be minimized."""
+- radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+- return -np.sum(radii)
++def constraint_function(z, n_circles):
++ """
++ Constraint function for SciPy minimize. All constraints must be >= 0.
++ - Wall constraints: ensure circles stay within [0,1]x[0,1].
++ - Non-overlap constraints: ensure no two circles overlap.
++ """
++ x, y, r = z[:n_circles], z[n_circles : 2 * n_circles], z[2 * n_circles : 3 * n_circles]
+
+- def _propose_move(self):
+- """
+- Proposes a new state by selecting a move type and applying it.
+- The step size is annealed with the temperature.
+- """
+- # Choose a move type based on weights
+- move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+-
+- # Anneal step size
+- t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+- current_step_size = self.max_step_size * t_progress
+-
+- return move_func(current_step_size)
++ # Wall constraints:
++ # x_i - r_i >= 0
++ # 1 - x_i - r_i >= 0
++ # y_i - r_i >= 0
++ # 1 - y_i - r_i >= 0
++ wall_constraints = np.concatenate([
++ x - r,
++ (1 - x) - r,
++ y - r,
++ (1 - y) - r
++ ])
+
+- def _move_single_circle(self, step_size):
+- """Move a single randomly chosen circle."""
+- new_centers = self.centers.copy()
+- idx = random.randint(0, self.n - 1)
+-
+- displacement = np.random.normal(0, step_size, size=2)
+- new_centers[idx] += displacement
+- new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+- return new_centers
+-
+- def _move_cluster(self, step_size):
+- """Move a small cluster of neighboring circles."""
+- new_centers = self.centers.copy()
+- cluster_size = self.config['cluster_size']
+-
+- # Pick a seed circle
+- seed_idx = random.randint(0, self.n - 1)
+-
+- # Find its neighbors
+- dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+- neighbor_indices = np.argsort(dists)[:cluster_size]
+-
+- # Apply a common displacement
+- displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+- new_centers[neighbor_indices] += displacement
+- new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+- return new_centers
+-
+- def _swap_circles(self, step_size):
+- """Swap the positions of two randomly chosen circles."""
+- new_centers = self.centers.copy()
+- if self.n < 2: return new_centers
+-
+- idx1, idx2 = random.sample(range(self.n), 2)
+- new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+- return new_centers
+-
+- def run(self):
+- """Executes the simulated annealing search."""
+- while self.temp > self.config['t_end']:
+- for _ in range(self.config['moves_per_temp']):
+- # Propose a new configuration
+- new_centers = self._propose_move()
+-
+- # Calculate its energy
+- new_energy = self._calculate_energy(new_centers)
+-
+- delta_e = new_energy - self.energy
+-
+- # Acceptance criterion
+- if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+- self.centers = new_centers
+- self.energy = new_energy
+-
+- # Update best-ever solution
+- if self.energy < self.best_energy:
+- self.best_energy = self.energy
+- self.best_centers = self.centers.copy()
+-
+- # Cool down the temperature
+- self.temp *= self.config['cooling_rate']
+-
+- return self.best_centers
++ # Non-overlap constraints: (x_i - x_j)^2 + (y_i - y_j)^2 >= (r_i + r_j)^2
++ # Formulated as: (x_i - x_j)^2 + (y_i - y_j)^2 - (r_i + r_j)^2 >= 0
++ pair_constraints = []
++ for i in range(n_circles):
++ for j in range(i + 1, n_circles):
++ dx = x[i] - x[j]
++ dy = y[i] - y[j]
++ dr = r[i] + r[j]
++ pair_constraints.append(dx * dx + dy * dy - dr * dr)
++
++ return np.concatenate([wall_constraints, np.array(pair_constraints)])
+
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
++ Constructs a packing of 26 circles using a multi-start Non-Linear Optimization approach.
+ """
+- n = 26
+- config = {
+- 'num_starts': 12, # Number of independent SA runs
+- 't_start': 0.05, # Initial temperature
+- 't_end': 1e-6, # Final temperature
+- 'cooling_rate': 0.998, # Geometric cooling factor
+- 'moves_per_temp': 60, # Number of moves at each temperature step
+- 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
+- 'radius_iter': 250, # Radius calculation iterations during search
+- 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+- 'cluster_size': 4,
+- }
++ n = 26 # Number of circles
++ num_starts = 15 # Number of different initial guesses for multi-start optimization
++
++ best_overall_centers = None
++ best_overall_sum_radii = -1.0
+
+- best_overall_centers = None
+- best_overall_score = -1.0
++ # Define bounds for x, y, and r for each circle
++ # x_i in [0, 1], y_i in [0, 1], r_i in [0, 0.5]
++ bounds = [(0, 1)] * n + [(0, 1)] * n + [(0, 0.5)] * n
+
+- # Multi-start loop
+- for i in range(config['num_starts']):
+- # --- Generate diverse initial configurations ---
+- initial_centers = np.zeros((n, 2))
+- if i < config['num_starts'] // 2:
+- # Start with a perturbed 5x5 grid + 1 extra circle
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for row in range(num_cells_side):
+- for col in range(num_cells_side):
+- initial_centers[k, 0] = (col + 0.5) * spacing
+- initial_centers[k, 1] = (row + 0.5) * spacing
+- k += 1
+- initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
++ # Total number of constraints
++ num_wall_constraints = 4 * n
++ num_pair_constraints = n * (n - 1) // 2
++ total_constraints = num_wall_constraints + num_pair_constraints
++
++ # Lower bounds for all constraints (all must be >= 0)
++ constraint_lower_bounds = np.zeros(total_constraints)
++ # Upper bounds for all constraints (can be infinity)
++ constraint_upper_bounds = np.full(total_constraints, np.inf)
++
++ # Create the NonlinearConstraint object once for efficiency
++ nlc = NonlinearConstraint(lambda z: constraint_function(z, n), constraint_lower_bounds, constraint_upper_bounds)
++
++ for start_idx in range(num_starts):
++ # --- Generate Initial Guess for Optimization ---
++ # Start with random center positions
++ initial_centers_xy = np.random.rand(n, 2)
++
++ # Calculate robust initial radii based on these centers.
++ # This provides a feasible starting point where circles don't overlap
++ # and are within bounds (for the initial centers).
++ initial_radii_guess = compute_max_radii(initial_centers_xy, max_iter=100, convergence_threshold=1e-5)
++
++ # Combine into the single 1D optimization variable vector z: [x_coords, y_coords, radii]
++ x0 = np.concatenate([initial_centers_xy[:, 0], initial_centers_xy[:, 1], initial_radii_guess])
++
++ # --- Run the Non-Linear Optimization ---
++ # SLSQP (Sequential Least Squares Programming) is chosen for its efficiency
++ # with non-linear objectives and constraints.
++ result = minimize(
++ fun=objective_function, # Function to minimize (negative sum of radii)
++ x0=x0, # Initial guess
++ args=(n,), # Additional arguments for objective_function
++ method='SLSQP', # Optimization method
++ bounds=bounds, # Bounds for each variable (x, y, r)
++ constraints=[nlc], # Nonlinear constraints
++ options={'ftol': 1e-7, 'maxiter': 750, 'disp': False} # Optimization parameters
++ )
++
++ # --- Evaluate and Update Best Solution ---
++ if result.success:
++ # Extract optimized centers and radii from the result
++ optimized_z = result.x
++ optimized_centers = np.column_stack([optimized_z[:n], optimized_z[n : 2 * n]])
+
+- # Place the 26th circle in a strategic corner or center
+- extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+- initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+- initial_centers = np.clip(initial_centers, 0.0, 1.0)
+- else:
+- # Start with a purely random configuration
+- initial_centers = np.random.rand(n, 2)
++ # Re-compute radii using the robust compute_max_radii function.
++ # This step is crucial because the NLP solver might leave small
++ # numerical inaccuracies or slack in constraints. This ensures
++ # the final radii are truly maximal and non-overlapping.
++ final_radii_for_eval = compute_max_radii(optimized_centers, max_iter=2500, convergence_threshold=1e-10)
++ current_sum_radii = np.sum(final_radii_for_eval)
+
+- # Run the annealer for this start
+- solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+- result_centers = solver.run()
+-
+- # Evaluate and update the best overall result
+- radii = compute_max_radii(result_centers, max_iter=2000)
+- score = np.sum(radii)
+-
+- if score > best_overall_score:
+- best_overall_score = score
+- best_overall_centers = result_centers
+-
+- # Final high-precision radius calculation for the best solution found
+- final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
++ if current_sum_radii > best_overall_sum_radii:
++ best_overall_sum_radii = current_sum_radii
++ best_overall_centers = optimized_centers
++ # else:
++ # print(f"Optimization for start {start_idx} failed: {result.message}")
++ # print(f"Current best sum: {best_overall_sum_radii}")
++
++
++ # Fallback in case no optimization was successful (should be rare)
++ if best_overall_centers is None:
++ best_overall_centers = np.random.rand(n, 2)
++
++ # Final, high-precision radius calculation for the best centers found
++ # This is the definitive calculation for the problem metric.
++ final_radii = compute_max_radii(best_overall_centers, max_iter=2500, convergence_threshold=1e-10)
+
+ return best_overall_centers, final_radii
+-
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..6133abf4fa4c74f373ba6b064ffdb333db5f4ad8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/main.py
@@ -0,0 +1,188 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+from scipy.optimize import minimize, NonlinearConstraint
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ This function is used for robust initial radius estimation and final evaluation,
+ but not directly within the core NLP optimization loop.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+
+ # Vectorized calculation for distances to improve performance
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Pre-calculated distance
+
+ # If circles overlap
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ # Scale down radii proportionally to resolve overlap
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii (safeguard)
+
+
+def objective_function(z, n_circles):
+ """
+ Objective function for SciPy minimize. We want to maximize the sum of radii,
+ so we minimize the negative sum of radii.
+ z is a 1D array: [x1..xn, y1..yn, r1..rn]
+ """
+ radii = z[2 * n_circles : 3 * n_circles]
+ return -np.sum(radii)
+
+def constraint_function(z, n_circles):
+ """
+ Constraint function for SciPy minimize. All constraints must be >= 0.
+ - Wall constraints: ensure circles stay within [0,1]x[0,1].
+ - Non-overlap constraints: ensure no two circles overlap.
+ """
+ x, y, r = z[:n_circles], z[n_circles : 2 * n_circles], z[2 * n_circles : 3 * n_circles]
+
+ # Wall constraints:
+ # x_i - r_i >= 0
+ # 1 - x_i - r_i >= 0
+ # y_i - r_i >= 0
+ # 1 - y_i - r_i >= 0
+ wall_constraints = np.concatenate([
+ x - r,
+ (1 - x) - r,
+ y - r,
+ (1 - y) - r
+ ])
+
+ # Non-overlap constraints: (x_i - x_j)^2 + (y_i - y_j)^2 >= (r_i + r_j)^2
+ # Formulated as: (x_i - x_j)^2 + (y_i - y_j)^2 - (r_i + r_j)^2 >= 0
+ pair_constraints = []
+ for i in range(n_circles):
+ for j in range(i + 1, n_circles):
+ dx = x[i] - x[j]
+ dy = y[i] - y[j]
+ dr = r[i] + r[j]
+ pair_constraints.append(dx * dx + dy * dy - dr * dr)
+
+ return np.concatenate([wall_constraints, np.array(pair_constraints)])
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Non-Linear Optimization approach.
+ """
+ n = 26 # Number of circles
+ num_starts = 15 # Number of different initial guesses for multi-start optimization
+
+ best_overall_centers = None
+ best_overall_sum_radii = -1.0
+
+ # Define bounds for x, y, and r for each circle
+ # x_i in [0, 1], y_i in [0, 1], r_i in [0, 0.5]
+ bounds = [(0, 1)] * n + [(0, 1)] * n + [(0, 0.5)] * n
+
+ # Total number of constraints
+ num_wall_constraints = 4 * n
+ num_pair_constraints = n * (n - 1) // 2
+ total_constraints = num_wall_constraints + num_pair_constraints
+
+ # Lower bounds for all constraints (all must be >= 0)
+ constraint_lower_bounds = np.zeros(total_constraints)
+ # Upper bounds for all constraints (can be infinity)
+ constraint_upper_bounds = np.full(total_constraints, np.inf)
+
+ # Create the NonlinearConstraint object once for efficiency
+ nlc = NonlinearConstraint(lambda z: constraint_function(z, n), constraint_lower_bounds, constraint_upper_bounds)
+
+ for start_idx in range(num_starts):
+ # --- Generate Initial Guess for Optimization ---
+ # Start with random center positions
+ initial_centers_xy = np.random.rand(n, 2)
+
+ # Calculate robust initial radii based on these centers.
+ # This provides a feasible starting point where circles don't overlap
+ # and are within bounds (for the initial centers).
+ initial_radii_guess = compute_max_radii(initial_centers_xy, max_iter=100, convergence_threshold=1e-5)
+
+ # Combine into the single 1D optimization variable vector z: [x_coords, y_coords, radii]
+ x0 = np.concatenate([initial_centers_xy[:, 0], initial_centers_xy[:, 1], initial_radii_guess])
+
+ # --- Run the Non-Linear Optimization ---
+ # SLSQP (Sequential Least Squares Programming) is chosen for its efficiency
+ # with non-linear objectives and constraints.
+ result = minimize(
+ fun=objective_function, # Function to minimize (negative sum of radii)
+ x0=x0, # Initial guess
+ args=(n,), # Additional arguments for objective_function
+ method='SLSQP', # Optimization method
+ bounds=bounds, # Bounds for each variable (x, y, r)
+ constraints=[nlc], # Nonlinear constraints
+ options={'ftol': 1e-7, 'maxiter': 750, 'disp': False} # Optimization parameters
+ )
+
+ # --- Evaluate and Update Best Solution ---
+ if result.success:
+ # Extract optimized centers and radii from the result
+ optimized_z = result.x
+ optimized_centers = np.column_stack([optimized_z[:n], optimized_z[n : 2 * n]])
+
+ # Re-compute radii using the robust compute_max_radii function.
+ # This step is crucial because the NLP solver might leave small
+ # numerical inaccuracies or slack in constraints. This ensures
+ # the final radii are truly maximal and non-overlapping.
+ final_radii_for_eval = compute_max_radii(optimized_centers, max_iter=2500, convergence_threshold=1e-10)
+ current_sum_radii = np.sum(final_radii_for_eval)
+
+ if current_sum_radii > best_overall_sum_radii:
+ best_overall_sum_radii = current_sum_radii
+ best_overall_centers = optimized_centers
+ # else:
+ # print(f"Optimization for start {start_idx} failed: {result.message}")
+ # print(f"Current best sum: {best_overall_sum_radii}")
+
+
+ # Fallback in case no optimization was successful (should be rare)
+ if best_overall_centers is None:
+ best_overall_centers = np.random.rand(n, 2)
+
+ # Final, high-precision radius calculation for the best centers found
+ # This is the definitive calculation for the problem metric.
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2500, convergence_threshold=1e-10)
+
+ return best_overall_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..af8c878afd2378eb48f3b44089ae4504fcfca048
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/original.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 12, # Number of independent SA runs
+ 't_start': 0.05, # Initial temperature
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.998, # Geometric cooling factor
+ 'moves_per_temp': 60, # Number of moves at each temperature step
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+ 'cluster_size': 4,
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Place the 26th circle in a strategic corner or center
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..0b557a2cbcef859658b2b8b472c390a46188c446
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results
+Run 1/1 completed in 21.19 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.2764949813150865
+ public: {'centers_str': ' centers[0] = (0.2065, 0.3842)\n centers[1] = (0.6238, 0.7419)\n centers[2] = (0.2147, 0.0845)\n centers[3] = (0.2513, 0.9030)\n centers[4] = (0.2717, 0.2323)\n centers[5] = (0.9397, 0.7396)\n centers[6] = (0.2762, 0.5665)\n centers[7] = (0.3183, 0.7423)\n centers[8] = (0.0576, 0.3780)\n centers[9] = (0.8970, 0.1030)\n centers[10] = (0.0657, 0.0657)\n centers[11] = (0.8971, 0.8971)\n centers[12] = (0.0777, 0.9223)\n centers[13] = (0.6953, 0.9010)\n centers[14] = (0.0990, 0.2270)\n centers[15] = (0.4719, 0.6424)\n centers[16] = (0.4066, 0.1089)\n centers[17] = (0.8553, 0.3472)\n centers[18] = (0.6552, 0.1419)\n centers[19] = (0.4722, 0.8743)\n centers[20] = (0.0890, 0.5211)\n centers[21] = (0.6828, 0.5522)\n centers[22] = (0.1208, 0.7285)\n centers[23] = (0.4598, 0.3745)\n centers[24] = (0.7894, 0.7370)\n centers[25] = (0.9017, 0.5857)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.2764949813150865}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/packing_viz.png
+ execution_time_mean: 21.189773737918586
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..1ba66934c8ce5f8f71d4456b1025ffd0d6550b38
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.2764949813150865,
+ "public": {
+ "centers_str": " centers[0] = (0.2065, 0.3842)\n centers[1] = (0.6238, 0.7419)\n centers[2] = (0.2147, 0.0845)\n centers[3] = (0.2513, 0.9030)\n centers[4] = (0.2717, 0.2323)\n centers[5] = (0.9397, 0.7396)\n centers[6] = (0.2762, 0.5665)\n centers[7] = (0.3183, 0.7423)\n centers[8] = (0.0576, 0.3780)\n centers[9] = (0.8970, 0.1030)\n centers[10] = (0.0657, 0.0657)\n centers[11] = (0.8971, 0.8971)\n centers[12] = (0.0777, 0.9223)\n centers[13] = (0.6953, 0.9010)\n centers[14] = (0.0990, 0.2270)\n centers[15] = (0.4719, 0.6424)\n centers[16] = (0.4066, 0.1089)\n centers[17] = (0.8553, 0.3472)\n centers[18] = (0.6552, 0.1419)\n centers[19] = (0.4722, 0.8743)\n centers[20] = (0.0890, 0.5211)\n centers[21] = (0.6828, 0.5522)\n centers[22] = (0.1208, 0.7285)\n centers[23] = (0.4598, 0.3745)\n centers[24] = (0.7894, 0.7370)\n centers[25] = (0.9017, 0.5857)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.2764949813150865
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/results/packing_viz.png",
+ "execution_time_mean": 21.189773737918586,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1efa795c8ccfdc4ae6c8d1b71ee4b40664a12361
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_109/rewrite.txt
@@ -0,0 +1,178 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+from scipy.optimize import minimize, NonlinearConstraint
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ This function is used for robust initial radius estimation and final evaluation,
+ but not directly within the core NLP optimization loop.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+
+ # Vectorized calculation for distances to improve performance
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Pre-calculated distance
+
+ # If circles overlap
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ # Scale down radii proportionally to resolve overlap
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii (safeguard)
+
+
+def objective_function(z, n_circles):
+ """
+ Objective function for SciPy minimize. We want to maximize the sum of radii,
+ so we minimize the negative sum of radii.
+ z is a 1D array: [x1..xn, y1..yn, r1..rn]
+ """
+ radii = z[2 * n_circles : 3 * n_circles]
+ return -np.sum(radii)
+
+def constraint_function(z, n_circles):
+ """
+ Constraint function for SciPy minimize. All constraints must be >= 0.
+ - Wall constraints: ensure circles stay within [0,1]x[0,1].
+ - Non-overlap constraints: ensure no two circles overlap.
+ """
+ x, y, r = z[:n_circles], z[n_circles : 2 * n_circles], z[2 * n_circles : 3 * n_circles]
+
+ # Wall constraints:
+ # x_i - r_i >= 0
+ # 1 - x_i - r_i >= 0
+ # y_i - r_i >= 0
+ # 1 - y_i - r_i >= 0
+ wall_constraints = np.concatenate([
+ x - r,
+ (1 - x) - r,
+ y - r,
+ (1 - y) - r
+ ])
+
+ # Non-overlap constraints: (x_i - x_j)^2 + (y_i - y_j)^2 >= (r_i + r_j)^2
+ # Formulated as: (x_i - x_j)^2 + (y_i - y_j)^2 - (r_i + r_j)^2 >= 0
+ pair_constraints = []
+ for i in range(n_circles):
+ for j in range(i + 1, n_circles):
+ dx = x[i] - x[j]
+ dy = y[i] - y[j]
+ dr = r[i] + r[j]
+ pair_constraints.append(dx * dx + dy * dy - dr * dr)
+
+ return np.concatenate([wall_constraints, np.array(pair_constraints)])
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Non-Linear Optimization approach.
+ """
+ n = 26 # Number of circles
+ num_starts = 15 # Number of different initial guesses for multi-start optimization
+
+ best_overall_centers = None
+ best_overall_sum_radii = -1.0
+
+ # Define bounds for x, y, and r for each circle
+ # x_i in [0, 1], y_i in [0, 1], r_i in [0, 0.5]
+ bounds = [(0, 1)] * n + [(0, 1)] * n + [(0, 0.5)] * n
+
+ # Total number of constraints
+ num_wall_constraints = 4 * n
+ num_pair_constraints = n * (n - 1) // 2
+ total_constraints = num_wall_constraints + num_pair_constraints
+
+ # Lower bounds for all constraints (all must be >= 0)
+ constraint_lower_bounds = np.zeros(total_constraints)
+ # Upper bounds for all constraints (can be infinity)
+ constraint_upper_bounds = np.full(total_constraints, np.inf)
+
+ # Create the NonlinearConstraint object once for efficiency
+ nlc = NonlinearConstraint(lambda z: constraint_function(z, n), constraint_lower_bounds, constraint_upper_bounds)
+
+ for start_idx in range(num_starts):
+ # --- Generate Initial Guess for Optimization ---
+ # Start with random center positions
+ initial_centers_xy = np.random.rand(n, 2)
+
+ # Calculate robust initial radii based on these centers.
+ # This provides a feasible starting point where circles don't overlap
+ # and are within bounds (for the initial centers).
+ initial_radii_guess = compute_max_radii(initial_centers_xy, max_iter=100, convergence_threshold=1e-5)
+
+ # Combine into the single 1D optimization variable vector z: [x_coords, y_coords, radii]
+ x0 = np.concatenate([initial_centers_xy[:, 0], initial_centers_xy[:, 1], initial_radii_guess])
+
+ # --- Run the Non-Linear Optimization ---
+ # SLSQP (Sequential Least Squares Programming) is chosen for its efficiency
+ # with non-linear objectives and constraints.
+ result = minimize(
+ fun=objective_function, # Function to minimize (negative sum of radii)
+ x0=x0, # Initial guess
+ args=(n,), # Additional arguments for objective_function
+ method='SLSQP', # Optimization method
+ bounds=bounds, # Bounds for each variable (x, y, r)
+ constraints=[nlc], # Nonlinear constraints
+ options={'ftol': 1e-7, 'maxiter': 750, 'disp': False} # Optimization parameters
+ )
+
+ # --- Evaluate and Update Best Solution ---
+ if result.success:
+ # Extract optimized centers and radii from the result
+ optimized_z = result.x
+ optimized_centers = np.column_stack([optimized_z[:n], optimized_z[n : 2 * n]])
+
+ # Re-compute radii using the robust compute_max_radii function.
+ # This step is crucial because the NLP solver might leave small
+ # numerical inaccuracies or slack in constraints. This ensures
+ # the final radii are truly maximal and non-overlapping.
+ final_radii_for_eval = compute_max_radii(optimized_centers, max_iter=2500, convergence_threshold=1e-10)
+ current_sum_radii = np.sum(final_radii_for_eval)
+
+ if current_sum_radii > best_overall_sum_radii:
+ best_overall_sum_radii = current_sum_radii
+ best_overall_centers = optimized_centers
+ # else:
+ # print(f"Optimization for start {start_idx} failed: {result.message}")
+ # print(f"Current best sum: {best_overall_sum_radii}")
+
+
+ # Fallback in case no optimization was successful (should be rare)
+ if best_overall_centers is None:
+ best_overall_centers = np.random.rand(n, 2)
+
+ # Final, high-precision radius calculation for the best centers found
+ # This is the definitive calculation for the problem metric.
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2500, convergence_threshold=1e-10)
+
+ return best_overall_centers, final_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9bd64ea3883836e676e4eb931a18d4cf271a383f
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..a3e7a734976e9893b475e43f2cdbba88d41335a6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/edit.diff
@@ -0,0 +1,214 @@
+--- a/original.py
++++ b/original.py
+@@ -1,155 +1,179 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
++Improved by combining insights from high-scoring prior programs:
++- Optimized initial placement of the 26th circle.
++- Reduced initial perturbation noise.
++- Tuned learning rate for fine-grained adjustments.
++- Increased iterations for radius calculation convergence.
++- Robust handling of very close circle centers in radius calculation.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- iterations = 250
+- learning_rate = 0.1
+- wall_repulsion_strength = 0.5
++ iterations = 250 # Number of simulation steps (kept from current)
++ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
++ wall_repulsion_strength = 0.5 # Kept from current
+
+ # --- Initialization ---
+- # Start with a perturbed grid. This is a good initial guess,
+- # and the perturbation helps break symmetry and escape a rigid structure.
++ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+- # Place the 26th circle somewhere in the middle to start.
+- centers[25] = [0.51, 0.49]
+
+- # Add small random noise to break grid symmetry
+- np.random.seed(42) # for reproducibility
+- centers += np.random.normal(0, 0.01, centers.shape)
+- centers = np.clip(centers, 0.01, 0.99)
++ # Place the 26th circle in a corner, as demonstrated effective in previous high-scoring
++ # constructor-based approaches (e.g., the 1.87 program). This efficiently uses boundary space.
++ centers[25] = [0.05, 0.05]
++
++ # Removed random noise. Starting from a stable, high-performing grid
++ # and allowing the physics simulation to refine it seems more effective
++ # than adding initial perturbations.
++
++ # Removed initial clipping (e.g., to 0.01-0.99). The physics simulation's clipping
++ # and wall repulsion will naturally keep circles within bounds, allowing them to
++ # touch the actual 0 and 1 edges.
+
+ # --- Simulation Loop ---
+- for k in range(iterations):
++ for sim_iter in range(iterations): # Renamed loop variable to avoid conflict with `k`
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+- if dist < 1e-9: continue
++
++ # If distance is near zero, centers are effectively identical.
++ # Radii calculation in compute_max_radii will handle this robustly.
++ # Here, we primarily avoid division by zero if radii[i] + radii[j] is also near zero.
++ if dist < 1e-9:
++ continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+- overlap = radii[i] - centers[i, 0]
+- if overlap > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap
++ overlap_left = radii[i] - centers[i, 0]
++ if overlap_left > 0:
++ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+- overlap = (centers[i, 0] + radii[i]) - 1.0
+- if overlap > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap
++ overlap_right = (centers[i, 0] + radii[i]) - 1.0
++ if overlap_right > 0:
++ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+- overlap = radii[i] - centers[i, 1]
+- if overlap > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap
++ overlap_bottom = radii[i] - centers[i, 1]
++ if overlap_bottom > 0:
++ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+- overlap = (centers[i, 1] + radii[i]) - 1.0
+- if overlap > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap
++ overlap_top = (centers[i, 1] + radii[i]) - 1.0
++ if overlap_top > 0:
++ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+- current_lr = learning_rate * (1.0 - (k / iterations))**2
++ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+- # 4. Enforce boundary constraints
++ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- for _ in range(100):
++ # Increased iterations for better convergence (from 100 to 200)
++ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+- # If current radii would cause overlap and dist is not zero
+- if radii[i] + radii[j] > dist and dist > 1e-9:
++ # Robust handling for circles with identical or very close centers
++ if dist < 1e-9:
++ # If centers are practically the same, they cannot both have positive radius.
++ # Force their radii to zero to resolve the severe overlap.
++ radii[i] = 0.0
++ radii[j] = 0.0
++ updated_in_pass = True
++ continue # Skip to next pair
++
++ # If current radii would cause overlap
++ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+- return np.maximum(radii, 0)
+-
++ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..efdd657e88d9db2fcfab9adc3be6d4f34acc7a36
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/main.py
@@ -0,0 +1,179 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250 # Number of simulation steps (kept from current)
+ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+ wall_repulsion_strength = 0.5 # Kept from current
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a corner, as demonstrated effective in previous high-scoring
+ # constructor-based approaches (e.g., the 1.87 program). This efficiently uses boundary space.
+ centers[25] = [0.05, 0.05]
+
+ # Removed random noise. Starting from a stable, high-performing grid
+ # and allowing the physics simulation to refine it seems more effective
+ # than adding initial perturbations.
+
+ # Removed initial clipping (e.g., to 0.01-0.99). The physics simulation's clipping
+ # and wall repulsion will naturally keep circles within bounds, allowing them to
+ # touch the actual 0 and 1 edges.
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations): # Renamed loop variable to avoid conflict with `k`
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ # If distance is near zero, centers are effectively identical.
+ # Radii calculation in compute_max_radii will handle this robustly.
+ # Here, we primarily avoid division by zero if radii[i] + radii[j] is also near zero.
+ if dist < 1e-9:
+ continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..e44b192582bde62068817fc151f7caba8f4de343
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/original.py
@@ -0,0 +1,155 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250
+ learning_rate = 0.1
+ wall_repulsion_strength = 0.5
+
+ # --- Initialization ---
+ # Start with a perturbed grid. This is a good initial guess,
+ # and the perturbation helps break symmetry and escape a rigid structure.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle somewhere in the middle to start.
+ centers[25] = [0.51, 0.49]
+
+ # Add small random noise to break grid symmetry
+ np.random.seed(42) # for reproducibility
+ centers += np.random.normal(0, 0.01, centers.shape)
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # --- Simulation Loop ---
+ for k in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap = radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..4ba490be9d97e6ba7a1011597b8c86a47f093df8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results
+Run 1/1 completed in 0.92 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.8730177484885533
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1000, 0.9000)\n centers[5] = (0.3000, 0.1000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.7000, 0.9000)\n centers[20] = (0.9000, 0.1000)\n centers[21] = (0.9000, 0.3000)\n centers[22] = (0.9000, 0.5000)\n centers[23] = (0.9000, 0.7000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.0500, 0.0500)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.8730177484885533}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/packing_viz.png
+ execution_time_mean: 0.9193816957995296
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..9168a0e7b2ce4ca3fb4df3f1ad67ab25f1e8ea68
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.8730177484885533,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1000, 0.9000)\n centers[5] = (0.3000, 0.1000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.7000, 0.9000)\n centers[20] = (0.9000, 0.1000)\n centers[21] = (0.9000, 0.3000)\n centers[22] = (0.9000, 0.5000)\n centers[23] = (0.9000, 0.7000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.0500, 0.0500)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.8730177484885533
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/results/packing_viz.png",
+ "execution_time_mean": 0.9193816957995296,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..da82d999f3dea14d26dd205392792892f728658e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_11/rewrite.txt
@@ -0,0 +1,169 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250 # Number of simulation steps (kept from current)
+ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+ wall_repulsion_strength = 0.5 # Kept from current
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a corner, as demonstrated effective in previous high-scoring
+ # constructor-based approaches (e.g., the 1.87 program). This efficiently uses boundary space.
+ centers[25] = [0.05, 0.05]
+
+ # Removed random noise. Starting from a stable, high-performing grid
+ # and allowing the physics simulation to refine it seems more effective
+ # than adding initial perturbations.
+
+ # Removed initial clipping (e.g., to 0.01-0.99). The physics simulation's clipping
+ # and wall repulsion will naturally keep circles within bounds, allowing them to
+ # touch the actual 0 and 1 edges.
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations): # Renamed loop variable to avoid conflict with `k`
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ # If distance is near zero, centers are effectively identical.
+ # Radii calculation in compute_max_radii will handle this robustly.
+ # Here, we primarily avoid division by zero if radii[i] + radii[j] is also near zero.
+ if dist < 1e-9:
+ continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..04c0aba0c42823578f9ed853ede96b1777df6b3e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/edit.diff
@@ -0,0 +1,495 @@
+--- a/original.py
++++ b/original.py
+@@ -1,258 +1,337 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ # --- Core Utility: Radius Calculation ---
+ # Adopted from constructor; more robust with convergence check.
+-def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-8):
++def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
++ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+- if dist < 1e-9:
++ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
++ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+- return np.maximum(radii, 0.0)
+-
+-# --- NEW STRUCTURE: Encapsulated Local Searcher ---
++ return np.maximum(radii, 0.0) # Ensure no negative radii
++
++# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+ class HybridLocalSearcher:
+ """
+- A force-directed local search optimizer. This class encapsulates the
+- two-phase physics simulation logic, making it a reusable component.
++ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
++ This component refines circle center positions to maximize radii sum in a local region.
++ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+- wall_strength=0.8, initial_growth_pressure=1.03,
++ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
++ # This phase uses artificially inflated radii to create strong repulsive forces,
++ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
++ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
++ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
++ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+- centers = np.clip(centers, 0.0, 1.0)
++ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
++ # After the aggressive exploration, this phase settles the packing without artificial pressure,
++ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+- radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
++ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+- centers = np.clip(centers, 0.0, 1.0)
++ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+- def _calculate_forces(self, centers, radii):
+- """Calculates repulsion forces from circle overlaps and wall proximity."""
++ def _calculate_forces(self, centers, effective_radii):
++ """
++ Calculates repulsion forces from circle overlaps and wall proximity.
++ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
++ """
+ forces = np.zeros_like(centers)
+
+- # Circle-to-circle repulsion
++ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+- dists[dists < 1e-9] = 1e-9
+-
+- radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+- overlaps = np.maximum(0, radii_sums - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- # Wall repulsion
+- forces[:, 0] += self.wall_strength * np.maximum(0, radii - centers[:, 0])
+- forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + radii) - 1.0)
+- forces[:, 1] += self.wall_strength * np.maximum(0, radii - centers[:, 1])
+- forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + radii) - 1.0)
++ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
++
++ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
++ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
++
++ force_magnitudes = overlaps # Force magnitude proportional to overlap
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
++ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
++
++ # Vectorized Wall repulsion (pushes circles away from boundaries)
++ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
++ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
++ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
++ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+-# --- GA Core Components ---
+-class Individual:
+- """Represents a single candidate packing solution."""
+- def __init__(self, centers_array, n_circles):
+- self.centers = np.array(centers_array).reshape(n_circles, 2)
+- self.radii = None
+- self.fitness = -1.0
+-
+- def evaluate_fitness(self, radius_iter_val):
+- self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+- self.fitness = np.sum(self.radii)
+- return self.fitness
+-
+-def initialize_population(n_circles, population_size):
+- """Initializes a diverse population, including known good starting points."""
+- population = []
++# --- Simulated Annealing specific components ---
++class SimulatedAnnealer:
++ """
++ Implements a Simulated Annealing algorithm for circle packing optimization.
++ It explores the configuration space using various move operators and
++ accepts 'worse' solutions probabilistically to escape local optima.
++ """
++ def __init__(self, n_circles,
++ initial_temp=100.0, cooling_rate=0.999, num_iterations=50000,
++ move_step_size=0.04, swap_prob=0.15, reinsert_prob=0.05,
++ cluster_move_prob=0.03, local_search_prob=0.01,
++ local_search_params=None, full_local_search_params=None):
++ self.n_circles = n_circles
++ self.T_initial = initial_temp
++ self.cooling_rate = cooling_rate
++ self.num_iterations = num_iterations
++ self.move_step_size = move_step_size
++ self.swap_prob = swap_prob
++ self.reinsert_prob = reinsert_prob
++ self.cluster_move_prob = cluster_move_prob
++ self.local_search_prob = local_search_prob
++
++ # Initialize local searchers with specific parameters for their roles
++ self.local_searcher_move = HybridLocalSearcher(**(local_search_params if local_search_params else {}))
++ self.local_searcher_final = HybridLocalSearcher(**(full_local_search_params if full_local_search_params else {}))
++
++ def _evaluate_fitness(self, centers, radius_max_iter):
++ """Calculates the sum of radii for a given center configuration."""
++ radii = compute_max_radii(centers, max_iter=radius_max_iter)
++ return np.sum(radii)
++
++ def _propose_move(self, centers, T):
++ """
++ Proposes a new configuration by applying one of several move types,
++ with probabilities influenced by annealing temperature.
++ """
++ new_centers = centers.copy()
++ move_roll = np.random.rand()
++
++ # Adapt jitter step size with temperature for annealing effect
++ current_move_step_size = self.move_step_size * (T / self.T_initial)
++
++ if move_roll < self.local_search_prob: # 1. Probabilistic Short Local Search
++ new_centers = self.local_searcher_move.optimize(new_centers)
++
++ elif move_roll < self.local_search_prob + self.swap_prob: # 2. Swap two random circles
++ i, j = np.random.choice(self.n_circles, 2, replace=False)
++ new_centers[[i, j]] = new_centers[[j, i]]
++
++ elif move_roll < self.local_search_prob + self.swap_prob + self.reinsert_prob: # 3. Re-insert one random circle
++ idx = np.random.randint(self.n_circles)
++ new_centers[idx] = np.random.rand(2)
++
++ elif move_roll < self.local_search_prob + self.swap_prob + self.reinsert_prob + self.cluster_move_prob: # 4. Cluster Move
++ center_idx = np.random.randint(self.n_circles)
++ num_cluster_circles = np.random.randint(2, min(5, self.n_circles -1)) # Cluster size 2-4
++
++ distances = np.linalg.norm(centers - centers[center_idx], axis=1)
++ closest_indices = np.argsort(distances)[1:num_cluster_circles+1]
++
++ cluster_indices = np.append(closest_indices, center_idx) # Include the base circle
++
++ move_vector = np.random.normal(0, current_move_step_size * 2, 2) # Larger move for clusters
++ new_centers[cluster_indices] += move_vector
++
++ else: # 5. Jitter one random circle (most common move type)
++ idx = np.random.randint(self.n_circles)
++ new_centers[idx] += np.random.normal(0, current_move_step_size, 2)
++
++ return np.clip(new_centers, 0.0, 1.0) # Ensure centers stay within bounds
++
++ def anneal(self, initial_centers):
++ """
++ Runs the simulated annealing process to find an optimized packing.
++ """
++ current_centers = initial_centers.copy()
++ current_fitness = self._evaluate_fitness(current_centers, radius_max_iter=50) # Faster eval for SA loop
++
++ best_centers = current_centers.copy()
++ best_fitness = current_fitness
++
++ T = self.T_initial
++ for k in range(self.num_iterations):
++ new_centers = self._propose_move(current_centers, T)
++ new_fitness = self._evaluate_fitness(new_centers, radius_max_iter=50)
++
++ # Metropolis-Hastings acceptance criterion for maximizing fitness
++ if new_fitness > current_fitness:
++ current_centers = new_centers
++ current_fitness = new_fitness
++ else:
++ # Accept worse solutions with probability decreasing with temperature
++ delta_E = (current_fitness - new_fitness) # Energy is -fitness, so delta_E > 0 for worse solutions
++ if np.random.rand() < np.exp(-delta_E / T):
++ current_centers = new_centers
++ current_fitness = new_fitness
++
++ if current_fitness > best_fitness:
++ best_centers = current_centers.copy()
++ best_fitness = current_fitness
++
++ T *= self.cooling_rate # Apply cooling schedule
++
++ # After SA, apply a final, thorough local search to the best configuration found
++ final_optimized_centers = self.local_searcher_final.optimize(best_centers.copy())
++ final_fitness_precise = self._evaluate_fitness(final_optimized_centers, radius_max_iter=600) # Precise evaluation
++
++ # Update if the final local search yields a better result
++ if final_fitness_precise > best_fitness:
++ best_centers = final_optimized_centers
++ best_fitness = final_fitness_precise
++
++ return best_centers, best_fitness
++
++def construct_packing():
++ """
++ Constructs a packing of 26 circles using a Multi-Start Simulated Annealing approach.
++ """
++ n_circles = 26
++
++ # --- Configuration for Simulated Annealing ---
++ sa_params = {
++ 'initial_temp': 200.0, # Higher initial temp for more exploration
++ 'cooling_rate': 0.9995, # Slower cooling rate for longer exploration
++ 'num_iterations': 100000, # Many iterations for SA cycle
++ 'move_step_size': 0.04, # Base step size for jitter (annealed)
++ 'swap_prob': 0.15, # Probability to swap two circles
++ 'reinsert_prob': 0.05, # Probability to re-insert one circle
++ 'cluster_move_prob': 0.03, # Probability for a cluster move
++ 'local_search_prob': 0.01, # Prob to run a *short* local search as a move
++ 'local_search_params': { # Parameters for local search used within SA moves (lighter)
++ 'sim_iter': 40,
++ 'fine_tune_iter': 15,
++ 'radius_sim_iter': 30,
++ 'initial_growth_pressure': 1.02,
++ 'fine_tune_lr': 0.0005,
++ 'learning_rate': 0.01,
++ 'wall_strength': 0.7
++ },
++ 'full_local_search_params': { # Parameters for the final, thorough local search (robust)
++ 'sim_iter': 500,
++ 'fine_tune_iter': 200,
++ 'radius_sim_iter': 100,
++ 'initial_growth_pressure': 1.05,
++ 'fine_tune_lr': 0.001,
++ 'learning_rate': 0.02,
++ 'wall_strength': 0.85
++ }
++ }
++
++ # --- Multi-Start Strategy ---
++ num_starts = 20
+
+- # 1. Strong baseline from 5x5 grid + interstitial
++ best_overall_centers = None
++ best_overall_sum_radii = -1.0
++
++ # Generate diverse initial configurations
++ initial_configs = []
++
++ # 1. Perturbed 5x5 grid + 1 extra circle (common good starting pattern)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+- grid_centers = np.zeros((n_circles, 2))
++ base_grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+- grid_centers[k, 0] = (i + 0.5) * spacing
+- grid_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+- grid_centers[n_circles - 1] = [spacing, spacing]
+- population.append(Individual(grid_centers, n_circles))
+-
+- # 2. Perturbed versions of the baseline
+- for _ in range(population_size // 10):
+- perturbed_centers = grid_centers + np.random.normal(0, 0.01, (n_circles, 2))
+- population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+-
+- # 3. Random configurations for diversity
+- while len(population) < population_size:
+- random_centers = np.random.rand(n_circles, 2)
+- population.append(Individual(random_centers, n_circles))
++ if k < n_circles - 1: # Fill 25 circles
++ base_grid_centers[k, 0] = (i + 0.5) * spacing
++ base_grid_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++ else:
++ break
++ # Place the 26th circle in a common interstitial region
++ if k < n_circles:
++ base_grid_centers[k] = [spacing * 1.5, spacing * 1.5]
++
++ # Add several perturbed versions of this grid
++ for _ in range(num_starts // 2):
++ perturbed_centers = base_grid_centers + np.random.normal(0, 0.02, (n_circles, 2))
++ initial_configs.append(np.clip(perturbed_centers, 0.0, 1.0))
++
++ # 2. Fill the remaining initial configurations with purely random centers
++ while len(initial_configs) < num_starts:
++ initial_configs.append(np.random.rand(n_circles, 2))
++
++ # Run Simulated Annealing for each initial configuration
++ for i, initial_centers in enumerate(initial_configs):
++ sa = SimulatedAnnealer(n_circles, **sa_params)
++ annealed_centers, annealed_fitness = sa.anneal(initial_centers)
++
++ if annealed_fitness > best_overall_sum_radii:
++ best_overall_sum_radii = annealed_fitness
++ best_overall_centers = annealed_centers.copy()
++
++ # Final high-precision radius calculation for the very best centers found
++ # This step is crucial as SA's internal fitness evaluations are approximate (fewer radius_max_iter)
++ final_radii = compute_max_radii(best_overall_centers, max_iter=800)
+
+- return population
+-
+-def select_parents(population, tournament_size):
+- """Selects two parents using tournament selection."""
+- tournament = np.random.choice(population, tournament_size, replace=False)
+- tournament = sorted(tournament, key=lambda ind: ind.fitness, reverse=True)
+- return tournament[0], tournament[1]
+-
+-def crossover(parent1, parent2, n_circles):
+- """Performs uniform crossover."""
+- child_centers = np.zeros((n_circles, 2))
+- mask = np.random.rand(n_circles) < 0.5
+- child_centers[mask] = parent1.centers[mask]
+- child_centers[~mask] = parent2.centers[~mask]
+- return Individual(child_centers, n_circles)
+-
+-def mutate(individual, mutation_rate, mutation_strength, n_circles):
+- """Applies simple Gaussian noise mutation."""
+- mutated_centers = individual.centers.copy()
+- for i in range(n_circles):
+- if np.random.rand() < mutation_rate:
+- mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+- individual.centers = np.clip(mutated_centers, 0.0, 1.0)
+- return individual
+-
+-# --- Main Orchestrator: Memetic Algorithm ---
+-def construct_packing():
+- """
+- Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+- a Genetic Algorithm with a powerful force-directed local search.
+- """
+- n_circles = 26
+-
+- # --- Hyperparameters ---
+- POPULATION_SIZE = 80
+- NUM_GENERATIONS = 500
+- MUTATION_RATE = 0.20
+- INITIAL_MUTATION_STRENGTH = 0.05
+- FINAL_MUTATION_STRENGTH = 0.001
+- TOURNAMENT_SIZE = 5
+- ELITISM_COUNT = 5
+- LOCAL_SEARCH_PROB = 0.1 # Probability to apply the powerful local search
+-
+- GA_RADIUS_ITER = 100
+- FINAL_RADIUS_ITER = 500
+-
+- # --- Initialization ---
+- population = initialize_population(n_circles, POPULATION_SIZE)
+- local_searcher = HybridLocalSearcher() # Using default (tuned) params
+-
+- for individual in population:
+- individual.evaluate_fitness(GA_RADIUS_ITER)
+-
+- best_individual_overall = max(population, key=lambda ind: ind.fitness)
+-
+- # --- Main GA Loop ---
+- for generation in range(NUM_GENERATIONS):
+- progress = generation / NUM_GENERATIONS
+- current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+- (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+-
+- new_population = []
+-
+- # Elitism
+- sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+- new_population.extend(sorted_population[:ELITISM_COUNT])
+-
+- # Generate new individuals
+- while len(new_population) < POPULATION_SIZE:
+- parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+- child = crossover(parent1, parent2, n_circles)
+-
+- # --- Hybrid Operator: Probabilistic Local Search and Simple Mutation ---
+- # 1. Probabilistically apply the powerful but expensive local search
+- if np.random.rand() < LOCAL_SEARCH_PROB:
+- child.centers = local_searcher.optimize(child.centers)
+-
+- # 2. Always apply a small standard mutation for diversity
+- child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+-
+- child.evaluate_fitness(GA_RADIUS_ITER)
+- new_population.append(child)
+-
+- population = new_population
+-
+- current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+- if current_best_in_gen.fitness > best_individual_overall.fitness:
+- best_individual_overall = current_best_in_gen
+-
+- # Final high-precision evaluation
+- best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+-
+- return best_individual_overall.centers, best_individual_overall.radii
++ return best_overall_centers, final_radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..7dd8fe99911db5de406c60f22f72a59731682463
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/main.py
@@ -0,0 +1,337 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- Simulated Annealing specific components ---
+class SimulatedAnnealer:
+ """
+ Implements a Simulated Annealing algorithm for circle packing optimization.
+ It explores the configuration space using various move operators and
+ accepts 'worse' solutions probabilistically to escape local optima.
+ """
+ def __init__(self, n_circles,
+ initial_temp=100.0, cooling_rate=0.999, num_iterations=50000,
+ move_step_size=0.04, swap_prob=0.15, reinsert_prob=0.05,
+ cluster_move_prob=0.03, local_search_prob=0.01,
+ local_search_params=None, full_local_search_params=None):
+ self.n_circles = n_circles
+ self.T_initial = initial_temp
+ self.cooling_rate = cooling_rate
+ self.num_iterations = num_iterations
+ self.move_step_size = move_step_size
+ self.swap_prob = swap_prob
+ self.reinsert_prob = reinsert_prob
+ self.cluster_move_prob = cluster_move_prob
+ self.local_search_prob = local_search_prob
+
+ # Initialize local searchers with specific parameters for their roles
+ self.local_searcher_move = HybridLocalSearcher(**(local_search_params if local_search_params else {}))
+ self.local_searcher_final = HybridLocalSearcher(**(full_local_search_params if full_local_search_params else {}))
+
+ def _evaluate_fitness(self, centers, radius_max_iter):
+ """Calculates the sum of radii for a given center configuration."""
+ radii = compute_max_radii(centers, max_iter=radius_max_iter)
+ return np.sum(radii)
+
+ def _propose_move(self, centers, T):
+ """
+ Proposes a new configuration by applying one of several move types,
+ with probabilities influenced by annealing temperature.
+ """
+ new_centers = centers.copy()
+ move_roll = np.random.rand()
+
+ # Adapt jitter step size with temperature for annealing effect
+ current_move_step_size = self.move_step_size * (T / self.T_initial)
+
+ if move_roll < self.local_search_prob: # 1. Probabilistic Short Local Search
+ new_centers = self.local_searcher_move.optimize(new_centers)
+
+ elif move_roll < self.local_search_prob + self.swap_prob: # 2. Swap two random circles
+ i, j = np.random.choice(self.n_circles, 2, replace=False)
+ new_centers[[i, j]] = new_centers[[j, i]]
+
+ elif move_roll < self.local_search_prob + self.swap_prob + self.reinsert_prob: # 3. Re-insert one random circle
+ idx = np.random.randint(self.n_circles)
+ new_centers[idx] = np.random.rand(2)
+
+ elif move_roll < self.local_search_prob + self.swap_prob + self.reinsert_prob + self.cluster_move_prob: # 4. Cluster Move
+ center_idx = np.random.randint(self.n_circles)
+ num_cluster_circles = np.random.randint(2, min(5, self.n_circles -1)) # Cluster size 2-4
+
+ distances = np.linalg.norm(centers - centers[center_idx], axis=1)
+ closest_indices = np.argsort(distances)[1:num_cluster_circles+1]
+
+ cluster_indices = np.append(closest_indices, center_idx) # Include the base circle
+
+ move_vector = np.random.normal(0, current_move_step_size * 2, 2) # Larger move for clusters
+ new_centers[cluster_indices] += move_vector
+
+ else: # 5. Jitter one random circle (most common move type)
+ idx = np.random.randint(self.n_circles)
+ new_centers[idx] += np.random.normal(0, current_move_step_size, 2)
+
+ return np.clip(new_centers, 0.0, 1.0) # Ensure centers stay within bounds
+
+ def anneal(self, initial_centers):
+ """
+ Runs the simulated annealing process to find an optimized packing.
+ """
+ current_centers = initial_centers.copy()
+ current_fitness = self._evaluate_fitness(current_centers, radius_max_iter=50) # Faster eval for SA loop
+
+ best_centers = current_centers.copy()
+ best_fitness = current_fitness
+
+ T = self.T_initial
+ for k in range(self.num_iterations):
+ new_centers = self._propose_move(current_centers, T)
+ new_fitness = self._evaluate_fitness(new_centers, radius_max_iter=50)
+
+ # Metropolis-Hastings acceptance criterion for maximizing fitness
+ if new_fitness > current_fitness:
+ current_centers = new_centers
+ current_fitness = new_fitness
+ else:
+ # Accept worse solutions with probability decreasing with temperature
+ delta_E = (current_fitness - new_fitness) # Energy is -fitness, so delta_E > 0 for worse solutions
+ if np.random.rand() < np.exp(-delta_E / T):
+ current_centers = new_centers
+ current_fitness = new_fitness
+
+ if current_fitness > best_fitness:
+ best_centers = current_centers.copy()
+ best_fitness = current_fitness
+
+ T *= self.cooling_rate # Apply cooling schedule
+
+ # After SA, apply a final, thorough local search to the best configuration found
+ final_optimized_centers = self.local_searcher_final.optimize(best_centers.copy())
+ final_fitness_precise = self._evaluate_fitness(final_optimized_centers, radius_max_iter=600) # Precise evaluation
+
+ # Update if the final local search yields a better result
+ if final_fitness_precise > best_fitness:
+ best_centers = final_optimized_centers
+ best_fitness = final_fitness_precise
+
+ return best_centers, best_fitness
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Multi-Start Simulated Annealing approach.
+ """
+ n_circles = 26
+
+ # --- Configuration for Simulated Annealing ---
+ sa_params = {
+ 'initial_temp': 200.0, # Higher initial temp for more exploration
+ 'cooling_rate': 0.9995, # Slower cooling rate for longer exploration
+ 'num_iterations': 100000, # Many iterations for SA cycle
+ 'move_step_size': 0.04, # Base step size for jitter (annealed)
+ 'swap_prob': 0.15, # Probability to swap two circles
+ 'reinsert_prob': 0.05, # Probability to re-insert one circle
+ 'cluster_move_prob': 0.03, # Probability for a cluster move
+ 'local_search_prob': 0.01, # Prob to run a *short* local search as a move
+ 'local_search_params': { # Parameters for local search used within SA moves (lighter)
+ 'sim_iter': 40,
+ 'fine_tune_iter': 15,
+ 'radius_sim_iter': 30,
+ 'initial_growth_pressure': 1.02,
+ 'fine_tune_lr': 0.0005,
+ 'learning_rate': 0.01,
+ 'wall_strength': 0.7
+ },
+ 'full_local_search_params': { # Parameters for the final, thorough local search (robust)
+ 'sim_iter': 500,
+ 'fine_tune_iter': 200,
+ 'radius_sim_iter': 100,
+ 'initial_growth_pressure': 1.05,
+ 'fine_tune_lr': 0.001,
+ 'learning_rate': 0.02,
+ 'wall_strength': 0.85
+ }
+ }
+
+ # --- Multi-Start Strategy ---
+ num_starts = 20
+
+ best_overall_centers = None
+ best_overall_sum_radii = -1.0
+
+ # Generate diverse initial configurations
+ initial_configs = []
+
+ # 1. Perturbed 5x5 grid + 1 extra circle (common good starting pattern)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles
+ base_grid_centers[k, 0] = (i + 0.5) * spacing
+ base_grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle in a common interstitial region
+ if k < n_circles:
+ base_grid_centers[k] = [spacing * 1.5, spacing * 1.5]
+
+ # Add several perturbed versions of this grid
+ for _ in range(num_starts // 2):
+ perturbed_centers = base_grid_centers + np.random.normal(0, 0.02, (n_circles, 2))
+ initial_configs.append(np.clip(perturbed_centers, 0.0, 1.0))
+
+ # 2. Fill the remaining initial configurations with purely random centers
+ while len(initial_configs) < num_starts:
+ initial_configs.append(np.random.rand(n_circles, 2))
+
+ # Run Simulated Annealing for each initial configuration
+ for i, initial_centers in enumerate(initial_configs):
+ sa = SimulatedAnnealer(n_circles, **sa_params)
+ annealed_centers, annealed_fitness = sa.anneal(initial_centers)
+
+ if annealed_fitness > best_overall_sum_radii:
+ best_overall_sum_radii = annealed_fitness
+ best_overall_centers = annealed_centers.copy()
+
+ # Final high-precision radius calculation for the very best centers found
+ # This step is crucial as SA's internal fitness evaluations are approximate (fewer radius_max_iter)
+ final_radii = compute_max_radii(best_overall_centers, max_iter=800)
+
+ return best_overall_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..b78ca62826187529538f17b9d3cbdede57b253af
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/original.py
@@ -0,0 +1,258 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# --- NEW STRUCTURE: Encapsulated Local Searcher ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer. This class encapsulates the
+ two-phase physics simulation logic, making it a reusable component.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.03,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning ---
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+ def _calculate_forces(self, centers, radii):
+ """Calculates repulsion forces from circle overlaps and wall proximity."""
+ forces = np.zeros_like(centers)
+
+ # Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Wall repulsion
+ forces[:, 0] += self.wall_strength * np.maximum(0, radii - centers[:, 0])
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + radii) - 1.0)
+ forces[:, 1] += self.wall_strength * np.maximum(0, radii - centers[:, 1])
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + radii) - 1.0)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population, including known good starting points."""
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + interstitial
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Perturbed versions of the baseline
+ for _ in range(population_size // 10):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.01, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Random configurations for diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two parents using tournament selection."""
+ tournament = np.random.choice(population, tournament_size, replace=False)
+ tournament = sorted(tournament, key=lambda ind: ind.fitness, reverse=True)
+ return tournament[0], tournament[1]
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover."""
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles) < 0.5
+ child_centers[mask] = parent1.centers[mask]
+ child_centers[~mask] = parent2.centers[~mask]
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies simple Gaussian noise mutation."""
+ mutated_centers = individual.centers.copy()
+ for i in range(n_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0)
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 500
+ MUTATION_RATE = 0.20
+ INITIAL_MUTATION_STRENGTH = 0.05
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 5
+ ELITISM_COUNT = 5
+ LOCAL_SEARCH_PROB = 0.1 # Probability to apply the powerful local search
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Using default (tuned) params
+
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main GA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate new individuals
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search and Simple Mutation ---
+ # 1. Probabilistically apply the powerful but expensive local search
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers)
+
+ # 2. Always apply a small standard mutation for diversity
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Final high-precision evaluation
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..30018ff2fbc8210aa3aa5d6b135b343b658c6798
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_110/rewrite.txt
@@ -0,0 +1,327 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- Simulated Annealing specific components ---
+class SimulatedAnnealer:
+ """
+ Implements a Simulated Annealing algorithm for circle packing optimization.
+ It explores the configuration space using various move operators and
+ accepts 'worse' solutions probabilistically to escape local optima.
+ """
+ def __init__(self, n_circles,
+ initial_temp=100.0, cooling_rate=0.999, num_iterations=50000,
+ move_step_size=0.04, swap_prob=0.15, reinsert_prob=0.05,
+ cluster_move_prob=0.03, local_search_prob=0.01,
+ local_search_params=None, full_local_search_params=None):
+ self.n_circles = n_circles
+ self.T_initial = initial_temp
+ self.cooling_rate = cooling_rate
+ self.num_iterations = num_iterations
+ self.move_step_size = move_step_size
+ self.swap_prob = swap_prob
+ self.reinsert_prob = reinsert_prob
+ self.cluster_move_prob = cluster_move_prob
+ self.local_search_prob = local_search_prob
+
+ # Initialize local searchers with specific parameters for their roles
+ self.local_searcher_move = HybridLocalSearcher(**(local_search_params if local_search_params else {}))
+ self.local_searcher_final = HybridLocalSearcher(**(full_local_search_params if full_local_search_params else {}))
+
+ def _evaluate_fitness(self, centers, radius_max_iter):
+ """Calculates the sum of radii for a given center configuration."""
+ radii = compute_max_radii(centers, max_iter=radius_max_iter)
+ return np.sum(radii)
+
+ def _propose_move(self, centers, T):
+ """
+ Proposes a new configuration by applying one of several move types,
+ with probabilities influenced by annealing temperature.
+ """
+ new_centers = centers.copy()
+ move_roll = np.random.rand()
+
+ # Adapt jitter step size with temperature for annealing effect
+ current_move_step_size = self.move_step_size * (T / self.T_initial)
+
+ if move_roll < self.local_search_prob: # 1. Probabilistic Short Local Search
+ new_centers = self.local_searcher_move.optimize(new_centers)
+
+ elif move_roll < self.local_search_prob + self.swap_prob: # 2. Swap two random circles
+ i, j = np.random.choice(self.n_circles, 2, replace=False)
+ new_centers[[i, j]] = new_centers[[j, i]]
+
+ elif move_roll < self.local_search_prob + self.swap_prob + self.reinsert_prob: # 3. Re-insert one random circle
+ idx = np.random.randint(self.n_circles)
+ new_centers[idx] = np.random.rand(2)
+
+ elif move_roll < self.local_search_prob + self.swap_prob + self.reinsert_prob + self.cluster_move_prob: # 4. Cluster Move
+ center_idx = np.random.randint(self.n_circles)
+ num_cluster_circles = np.random.randint(2, min(5, self.n_circles -1)) # Cluster size 2-4
+
+ distances = np.linalg.norm(centers - centers[center_idx], axis=1)
+ closest_indices = np.argsort(distances)[1:num_cluster_circles+1]
+
+ cluster_indices = np.append(closest_indices, center_idx) # Include the base circle
+
+ move_vector = np.random.normal(0, current_move_step_size * 2, 2) # Larger move for clusters
+ new_centers[cluster_indices] += move_vector
+
+ else: # 5. Jitter one random circle (most common move type)
+ idx = np.random.randint(self.n_circles)
+ new_centers[idx] += np.random.normal(0, current_move_step_size, 2)
+
+ return np.clip(new_centers, 0.0, 1.0) # Ensure centers stay within bounds
+
+ def anneal(self, initial_centers):
+ """
+ Runs the simulated annealing process to find an optimized packing.
+ """
+ current_centers = initial_centers.copy()
+ current_fitness = self._evaluate_fitness(current_centers, radius_max_iter=50) # Faster eval for SA loop
+
+ best_centers = current_centers.copy()
+ best_fitness = current_fitness
+
+ T = self.T_initial
+ for k in range(self.num_iterations):
+ new_centers = self._propose_move(current_centers, T)
+ new_fitness = self._evaluate_fitness(new_centers, radius_max_iter=50)
+
+ # Metropolis-Hastings acceptance criterion for maximizing fitness
+ if new_fitness > current_fitness:
+ current_centers = new_centers
+ current_fitness = new_fitness
+ else:
+ # Accept worse solutions with probability decreasing with temperature
+ delta_E = (current_fitness - new_fitness) # Energy is -fitness, so delta_E > 0 for worse solutions
+ if np.random.rand() < np.exp(-delta_E / T):
+ current_centers = new_centers
+ current_fitness = new_fitness
+
+ if current_fitness > best_fitness:
+ best_centers = current_centers.copy()
+ best_fitness = current_fitness
+
+ T *= self.cooling_rate # Apply cooling schedule
+
+ # After SA, apply a final, thorough local search to the best configuration found
+ final_optimized_centers = self.local_searcher_final.optimize(best_centers.copy())
+ final_fitness_precise = self._evaluate_fitness(final_optimized_centers, radius_max_iter=600) # Precise evaluation
+
+ # Update if the final local search yields a better result
+ if final_fitness_precise > best_fitness:
+ best_centers = final_optimized_centers
+ best_fitness = final_fitness_precise
+
+ return best_centers, best_fitness
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Multi-Start Simulated Annealing approach.
+ """
+ n_circles = 26
+
+ # --- Configuration for Simulated Annealing ---
+ sa_params = {
+ 'initial_temp': 200.0, # Higher initial temp for more exploration
+ 'cooling_rate': 0.9995, # Slower cooling rate for longer exploration
+ 'num_iterations': 100000, # Many iterations for SA cycle
+ 'move_step_size': 0.04, # Base step size for jitter (annealed)
+ 'swap_prob': 0.15, # Probability to swap two circles
+ 'reinsert_prob': 0.05, # Probability to re-insert one circle
+ 'cluster_move_prob': 0.03, # Probability for a cluster move
+ 'local_search_prob': 0.01, # Prob to run a *short* local search as a move
+ 'local_search_params': { # Parameters for local search used within SA moves (lighter)
+ 'sim_iter': 40,
+ 'fine_tune_iter': 15,
+ 'radius_sim_iter': 30,
+ 'initial_growth_pressure': 1.02,
+ 'fine_tune_lr': 0.0005,
+ 'learning_rate': 0.01,
+ 'wall_strength': 0.7
+ },
+ 'full_local_search_params': { # Parameters for the final, thorough local search (robust)
+ 'sim_iter': 500,
+ 'fine_tune_iter': 200,
+ 'radius_sim_iter': 100,
+ 'initial_growth_pressure': 1.05,
+ 'fine_tune_lr': 0.001,
+ 'learning_rate': 0.02,
+ 'wall_strength': 0.85
+ }
+ }
+
+ # --- Multi-Start Strategy ---
+ num_starts = 20
+
+ best_overall_centers = None
+ best_overall_sum_radii = -1.0
+
+ # Generate diverse initial configurations
+ initial_configs = []
+
+ # 1. Perturbed 5x5 grid + 1 extra circle (common good starting pattern)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles
+ base_grid_centers[k, 0] = (i + 0.5) * spacing
+ base_grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle in a common interstitial region
+ if k < n_circles:
+ base_grid_centers[k] = [spacing * 1.5, spacing * 1.5]
+
+ # Add several perturbed versions of this grid
+ for _ in range(num_starts // 2):
+ perturbed_centers = base_grid_centers + np.random.normal(0, 0.02, (n_circles, 2))
+ initial_configs.append(np.clip(perturbed_centers, 0.0, 1.0))
+
+ # 2. Fill the remaining initial configurations with purely random centers
+ while len(initial_configs) < num_starts:
+ initial_configs.append(np.random.rand(n_circles, 2))
+
+ # Run Simulated Annealing for each initial configuration
+ for i, initial_centers in enumerate(initial_configs):
+ sa = SimulatedAnnealer(n_circles, **sa_params)
+ annealed_centers, annealed_fitness = sa.anneal(initial_centers)
+
+ if annealed_fitness > best_overall_sum_radii:
+ best_overall_sum_radii = annealed_fitness
+ best_overall_centers = annealed_centers.copy()
+
+ # Final high-precision radius calculation for the very best centers found
+ # This step is crucial as SA's internal fitness evaluations are approximate (fewer radius_max_iter)
+ final_radii = compute_max_radii(best_overall_centers, max_iter=800)
+
+ return best_overall_centers, final_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..27a678c5c14165559f766fbf8256be3260a9e89d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/edit.diff
@@ -0,0 +1,333 @@
+--- a/original.py
++++ b/original.py
+@@ -1,306 +1,314 @@
+ # EVOLVE-BLOCK-START
+ """
+ This solution implements a Memetic Algorithm (MA), which combines a Genetic
+ Algorithm (GA) for global exploration with a powerful, physics-based local search
+ for exploitation. This approach hybridizes the successful population-based
+ framework of prior submissions with the refined, two-stage force-directed
+ simulation from the current program, which will be repurposed as the local
+ search operator.
+ """
+
+ import numpy as np
+
+ class LocalSearchRefiner:
+ """
+ Performs a fast, two-stage, force-directed local refinement on a set of centers.
+ This is adapted from the full simulation of the parent program to act as a
+ powerful local search operator within the Memetic Algorithm.
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ n = refined_centers.shape[0]
+
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
+ progress = sim_iter / self.config['lsr_aggressive_iter']
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning with subtle growth pressure ---
+ for sim_iter_ft in range(self.config['lsr_finetune_iter']):
+ progress = sim_iter_ft / self.config['lsr_finetune_iter']
+ current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
+ current_pressure = self.config['lsr_finetune_pressure_end'] + \
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+ class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+- num_grid_based = self.config['population_size'] // 3
++ num_grid_based = self.config['population_size'] // 2
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++
++ # Expanded list of strategic candidate positions for the 26th circle
++ candidate_extra_positions = [
++ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
++ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
++ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
++ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+- # Place 26th circle in a random-ish interstitial void
+- interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
+- extra_pos = interstitial_points[i % len(interstitial_points)]
+- centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
++ # Place 26th circle in one of the diverse candidate positions
++ extra_pos = candidate_extra_positions[i % len(candidate_extra_positions)]
++ centers[25] = np.array(extra_pos) + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
++ # Fill the rest with purely random configurations for diversity
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ fitnesses = []
+ for ind in self.population:
+ radii = compute_max_radii(ind)
+ fitnesses.append(np.sum(radii))
+ self.fitnesses = np.array(fitnesses)
+
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+- """Performs blend crossover (BLX-alpha)."""
+- alpha = self.config['crossover_alpha']
+- child = alpha * p1 + (1.0 - alpha) * p2
+- return np.clip(child, 0.0, 1.0)
++ """Performs uniform crossover, taking whole circles from either parent."""
++ child = p1.copy()
++ mask = np.random.rand(self.n) < 0.5
++ child[mask] = p2[mask]
++ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over best individuals
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # --- GA Parameters ---
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 4,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.35, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+- 'crossover_alpha': 0.5, # For BLX-alpha crossover
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ # --- Local Search Refiner (LSR) Parameters ---
+- 'lsr_aggressive_iter': 40,
+- 'lsr_aggressive_lr': 0.025,
++ 'lsr_aggressive_iter': 120,
++ 'lsr_aggressive_lr': 0.02,
+ 'lsr_aggressive_pressure_start': 1.06,
+ 'lsr_aggressive_pressure_end': 1.001,
+- 'lsr_finetune_iter': 80,
++ 'lsr_finetune_iter': 180,
+ 'lsr_finetune_lr': 0.001,
+ 'lsr_finetune_pressure_start': 1.0008,
+ 'lsr_finetune_pressure_end': 1.00001,
+- 'lsr_wall_strength': 0.5,
++ 'lsr_wall_strength': 0.6,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers)
+
+ return best_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..11399221354e0d9c3fa10b361c7a190f6d1e6991
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/main.py
@@ -0,0 +1,314 @@
+# EVOLVE-BLOCK-START
+"""
+This solution implements a Memetic Algorithm (MA), which combines a Genetic
+Algorithm (GA) for global exploration with a powerful, physics-based local search
+for exploitation. This approach hybridizes the successful population-based
+framework of prior submissions with the refined, two-stage force-directed
+simulation from the current program, which will be repurposed as the local
+search operator.
+"""
+
+import numpy as np
+
+class LocalSearchRefiner:
+ """
+ Performs a fast, two-stage, force-directed local refinement on a set of centers.
+ This is adapted from the full simulation of the parent program to act as a
+ powerful local search operator within the Memetic Algorithm.
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ n = refined_centers.shape[0]
+
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
+ progress = sim_iter / self.config['lsr_aggressive_iter']
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning with subtle growth pressure ---
+ for sim_iter_ft in range(self.config['lsr_finetune_iter']):
+ progress = sim_iter_ft / self.config['lsr_finetune_iter']
+ current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
+ current_pressure = self.config['lsr_finetune_pressure_end'] + \
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 2
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ # Expanded list of strategic candidate positions for the 26th circle
+ candidate_extra_positions = [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+ # Place 26th circle in one of the diverse candidate positions
+ extra_pos = candidate_extra_positions[i % len(candidate_extra_positions)]
+ centers[25] = np.array(extra_pos) + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ # Fill the rest with purely random configurations for diversity
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ fitnesses = []
+ for ind in self.population:
+ radii = compute_max_radii(ind)
+ fitnesses.append(np.sum(radii))
+ self.fitnesses = np.array(fitnesses)
+
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over best individuals
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # --- GA Parameters ---
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 4,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.35, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ # --- Local Search Refiner (LSR) Parameters ---
+ 'lsr_aggressive_iter': 120,
+ 'lsr_aggressive_lr': 0.02,
+ 'lsr_aggressive_pressure_start': 1.06,
+ 'lsr_aggressive_pressure_end': 1.001,
+ 'lsr_finetune_iter': 180,
+ 'lsr_finetune_lr': 0.001,
+ 'lsr_finetune_pressure_start': 1.0008,
+ 'lsr_finetune_pressure_end': 1.00001,
+ 'lsr_wall_strength': 0.6,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers)
+
+ return best_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..6d95001722637c0a9df674b7dd049669085ab9e8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/original.py
@@ -0,0 +1,306 @@
+# EVOLVE-BLOCK-START
+"""
+This solution implements a Memetic Algorithm (MA), which combines a Genetic
+Algorithm (GA) for global exploration with a powerful, physics-based local search
+for exploitation. This approach hybridizes the successful population-based
+framework of prior submissions with the refined, two-stage force-directed
+simulation from the current program, which will be repurposed as the local
+search operator.
+"""
+
+import numpy as np
+
+class LocalSearchRefiner:
+ """
+ Performs a fast, two-stage, force-directed local refinement on a set of centers.
+ This is adapted from the full simulation of the parent program to act as a
+ powerful local search operator within the Memetic Algorithm.
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ n = refined_centers.shape[0]
+
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
+ progress = sim_iter / self.config['lsr_aggressive_iter']
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning with subtle growth pressure ---
+ for sim_iter_ft in range(self.config['lsr_finetune_iter']):
+ progress = sim_iter_ft / self.config['lsr_finetune_iter']
+ current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
+ current_pressure = self.config['lsr_finetune_pressure_end'] + \
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 3
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+ # Place 26th circle in a random-ish interstitial void
+ interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ fitnesses = []
+ for ind in self.population:
+ radii = compute_max_radii(ind)
+ fitnesses.append(np.sum(radii))
+ self.fitnesses = np.array(fitnesses)
+
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs blend crossover (BLX-alpha)."""
+ alpha = self.config['crossover_alpha']
+ child = alpha * p1 + (1.0 - alpha) * p2
+ return np.clip(child, 0.0, 1.0)
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over best individuals
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # --- GA Parameters ---
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 4,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.35, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'crossover_alpha': 0.5, # For BLX-alpha crossover
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ # --- Local Search Refiner (LSR) Parameters ---
+ 'lsr_aggressive_iter': 40,
+ 'lsr_aggressive_lr': 0.025,
+ 'lsr_aggressive_pressure_start': 1.06,
+ 'lsr_aggressive_pressure_end': 1.001,
+ 'lsr_finetune_iter': 80,
+ 'lsr_finetune_lr': 0.001,
+ 'lsr_finetune_pressure_start': 1.0008,
+ 'lsr_finetune_pressure_end': 1.00001,
+ 'lsr_wall_strength': 0.5,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers)
+
+ return best_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5459531acb04b28b2472895045eb0575f1473e1a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_111/search_replace.txt
@@ -0,0 +1,143 @@
+Based on my analysis of the current Memetic Algorithm's performance and structure, I've identified three key areas for improvement. The current blend crossover is destructive to good circle arrangements, the local search is too weak to be effective, and the population initialization lacks diversity. My proposed changes aim to rectify these issues by reintroducing proven strategies from prior high-scoring versions.
+
+### 1. Revert to Uniform Crossover
+
+The first change is to replace the Blend Crossover (BLX-alpha) with Uniform Crossover. BLX-alpha averages parental coordinates, which disrupts the discrete nature of circle positions and breaks well-formed sub-packings (schemata). Uniform Crossover, by inheriting entire circles from either parent, preserves these crucial building blocks, which is essential for effective evolution in this problem domain.
+
+### 2. Strengthen Local Search and Adjust Configuration
+
+The second change significantly enhances the power of the memetic local search component. The current iteration counts are too low to allow for meaningful convergence. I am increasing the iterations for both the aggressive and fine-tuning stages of the `LocalSearchRefiner`. This allows the algorithm to better exploit promising regions found by the global search. Concurrently, I am removing the now-obsolete `crossover_alpha` parameter from the configuration.
+
+### 3. Diversify Population Initialization
+
+The final change improves the quality and diversity of the initial population. I am increasing the proportion of strategically-generated individuals from one-third to one-half and expanding the set of candidate starting positions for the 26th circle. By seeding the initial population with a wider variety of high-potential configurations (including corner, edge, and interstitial placements), we provide the genetic algorithm with superior raw material, increasing the probability of discovering high-quality solutions.
+
+These three changes synergize to create a more robust and effective algorithm: better initial states, a crossover operator that preserves good structures, and a powerful local search to refine them.
+
+
+revert_to_uniform_crossover
+
+
+The current blend crossover (BLX-alpha) averages the coordinates of circles between parents. This is a highly destructive operation for geometric packing problems, as it fails to preserve well-formed "building blocks" (groups of circles in a good relative arrangement). By reverting to uniform crossover, where each circle is inherited as a complete unit from one of the two parents, we promote the propagation of good substructures (schemata) through generations. This is a more standard and effective approach for this problem type and was used in previous higher-scoring versions.
+
+
+<<<<<<< SEARCH
+ def _crossover(self, p1, p2):
+ """Performs blend crossover (BLX-alpha)."""
+ alpha = self.config['crossover_alpha']
+ child = alpha * p1 + (1.0 - alpha) * p2
+ return np.clip(child, 0.0, 1.0)
+=======
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+>>>>>>> REPLACE
+
+
+strengthen_local_search
+
+
+The current local search refiner is too weak, with very few iterations (40 aggressive, 80 fine-tune). This undermines the "memetic" principle of combining global exploration with powerful local exploitation. I am significantly increasing the number of iterations for both stages of the local search (`lsr_aggressive_iter` to 120, `lsr_finetune_iter` to 180), slightly reducing the aggressive learning rate to maintain stability over the longer run, and increasing wall strength to better utilize boundaries. I am also removing the `crossover_alpha` parameter, which became obsolete after reverting to uniform crossover. This change makes the local search a more potent tool for refining solutions.
+
+
+<<<<<<< SEARCH
+ 'crossover_rate': 0.9,
+ 'crossover_alpha': 0.5, # For BLX-alpha crossover
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ # --- Local Search Refiner (LSR) Parameters ---
+ 'lsr_aggressive_iter': 40,
+ 'lsr_aggressive_lr': 0.025,
+ 'lsr_aggressive_pressure_start': 1.06,
+ 'lsr_aggressive_pressure_end': 1.001,
+ 'lsr_finetune_iter': 80,
+ 'lsr_finetune_lr': 0.001,
+ 'lsr_finetune_pressure_start': 1.0008,
+ 'lsr_finetune_pressure_end': 1.00001,
+ 'lsr_wall_strength': 0.5,
+=======
+ 'crossover_rate': 0.9,
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ # --- Local Search Refiner (LSR) Parameters ---
+ 'lsr_aggressive_iter': 120,
+ 'lsr_aggressive_lr': 0.02,
+ 'lsr_aggressive_pressure_start': 1.06,
+ 'lsr_aggressive_pressure_end': 1.001,
+ 'lsr_finetune_iter': 180,
+ 'lsr_finetune_lr': 0.001,
+ 'lsr_finetune_pressure_start': 1.0008,
+ 'lsr_finetune_pressure_end': 1.00001,
+ 'lsr_wall_strength': 0.6,
+>>>>>>> REPLACE
+
+
+diversify_population_initialization
+
+
+The initial population provides the raw material for evolution. The current initialization is suboptimal, using only 1/3 grid-based starts and a small set of 5 candidate positions for the 26th circle. To improve this, I am increasing the proportion of strategically-generated individuals to 50% of the population. Furthermore, I am replacing the small list of candidate points with a much larger and more diverse set, inspired by previous high-scoring versions. This set includes corner-proximate points, edge-midpoints, and interstitial grid points, providing the algorithm with a much richer and more promising initial gene pool to explore.
+
+
+<<<<<<< SEARCH
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 3
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+ # Place 26th circle in a random-ish interstitial void
+ interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+=======
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 2
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ # Expanded list of strategic candidate positions for the 26th circle
+ candidate_extra_positions = [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+ # Place 26th circle in one of the diverse candidate positions
+ extra_pos = candidate_extra_positions[i % len(candidate_extra_positions)]
+ centers[25] = np.array(extra_pos) + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ # Fill the rest with purely random configurations for diversity
+ self.population.append(np.random.rand(self.n, 2))
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bd9ac07b2daa3a6af541647815c73c33c4ab2bec
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..856d92451ae3b4007538152341d74e15d0375843
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/edit.diff
@@ -0,0 +1,389 @@
+--- a/original.py
++++ b/original.py
+@@ -1,306 +1,315 @@
+ # EVOLVE-BLOCK-START
+ """
+ This solution implements a Memetic Algorithm (MA), which combines a Genetic
+ Algorithm (GA) for global exploration with a powerful, physics-based local search
+ for exploitation. This approach hybridizes the successful population-based
+ framework of prior submissions with the refined, two-stage force-directed
+ simulation from the current program, which will be repurposed as the local
+ search operator.
+ """
+
+ import numpy as np
+
+ class LocalSearchRefiner:
+ """
+ Performs a fast, two-stage, force-directed local refinement on a set of centers.
+ This is adapted from the full simulation of the parent program to act as a
+ powerful local search operator within the Memetic Algorithm.
+ """
+ def __init__(self, config):
+ self.config = config
++ self.lsr_radius_iter = config['lsr_radius_iter']
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ n = refined_centers.shape[0]
+
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
+ progress = sim_iter / self.config['lsr_aggressive_iter']
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+
+- radii = compute_max_radii(refined_centers)
++ radii = compute_max_radii(refined_centers, max_iter=self.lsr_radius_iter, convergence_threshold=1e-7)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning with subtle growth pressure ---
+ for sim_iter_ft in range(self.config['lsr_finetune_iter']):
+ progress = sim_iter_ft / self.config['lsr_finetune_iter']
+ current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
+ current_pressure = self.config['lsr_finetune_pressure_end'] + \
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+
+- radii = compute_max_radii(refined_centers)
++ radii = compute_max_radii(refined_centers, max_iter=self.lsr_radius_iter, convergence_threshold=1e-7)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+ class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 3
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+- # Place 26th circle in a random-ish interstitial void
+- interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
++ # Place 26th circle in a random-ish interstitial void from an expanded list
++ interstitial_points = [
++ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5], # Corners and center
++ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Near corners
++ [0.5, 0.1], [0.1, 0.5], [0.9, 0.5], [0.5, 0.9], # Midpoints of edges
++ [0.3, 0.3], [0.7, 0.7], [0.3, 0.7], [0.7, 0.3] # Inner quadrants
++ ]
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ fitnesses = []
++ # Use configurable radius iterations for population evaluation
+ for ind in self.population:
+- radii = compute_max_radii(ind)
++ radii = compute_max_radii(ind, max_iter=self.config['ma_radius_iter_eval'], convergence_threshold=1e-7)
+ fitnesses.append(np.sum(radii))
+ self.fitnesses = np.array(fitnesses)
+
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+- """Performs blend crossover (BLX-alpha)."""
+- alpha = self.config['crossover_alpha']
+- child = alpha * p1 + (1.0 - alpha) * p2
+- return np.clip(child, 0.0, 1.0)
++ """Performs uniform crossover, taking whole circles from either parent."""
++ child = p1.copy()
++ mask = np.random.rand(self.n) < 0.5
++ child[mask] = p2[mask]
++ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over best individuals
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # --- GA Parameters ---
+- 'population_size': 80,
+- 'generations': 350,
+- 'elite_count': 4,
+- 'tournament_size': 6,
+- 'mutation_rate': 0.35, # Per-circle mutation probability
+- 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
+- 'mut_strength_start': 0.1,
+- 'mut_strength_end': 0.001,
+- 'crossover_rate': 0.9,
+- 'crossover_alpha': 0.5, # For BLX-alpha crossover
++ 'population_size': 100, # Increased population size for broader exploration
++ 'generations': 400, # More generations to allow for convergence
++ 'elite_count': 5, # Number of elite individuals to carry over
++ 'tournament_size': 7, # Increased tournament size for higher selection pressure
++ 'mutation_rate': 0.4, # Per-circle mutation probability, slightly higher
++ 'jump_mutation_prob': 0.04, # Increased probability of a random reset for a circle
++ 'mut_strength_start': 0.12, # Higher initial mutation strength
++ 'mut_strength_end': 0.0005, # Lower final mutation strength for fine-tuning
++ 'crossover_rate': 0.95, # Higher crossover rate
++
+ # --- Memetic Parameters ---
+- 'local_search_prob': 0.20, # Probability of applying local search to a new child
++ 'local_search_prob': 0.25, # Increased probability of applying local search
++
++ # --- Radius Calculation Iterations ---
++ 'ma_radius_iter_eval': 300, # Iterations for compute_max_radii during GA evaluation
++ 'lsr_radius_iter': 150, # Iterations for compute_max_radii within LocalSearchRefiner
++
+ # --- Local Search Refiner (LSR) Parameters ---
+- 'lsr_aggressive_iter': 40,
+- 'lsr_aggressive_lr': 0.025,
+- 'lsr_aggressive_pressure_start': 1.06,
++ 'lsr_aggressive_iter': 50, # More aggressive iterations
++ 'lsr_aggressive_lr': 0.03, # Slightly higher aggressive learning rate
++ 'lsr_aggressive_pressure_start': 1.07, # Higher initial pressure
+ 'lsr_aggressive_pressure_end': 1.001,
+- 'lsr_finetune_iter': 80,
+- 'lsr_finetune_lr': 0.001,
+- 'lsr_finetune_pressure_start': 1.0008,
+- 'lsr_finetune_pressure_end': 1.00001,
+- 'lsr_wall_strength': 0.5,
++ 'lsr_finetune_iter': 100, # More fine-tune iterations
++ 'lsr_finetune_lr': 0.0015, # Slightly higher fine-tune learning rate
++ 'lsr_finetune_pressure_start': 1.001,
++ 'lsr_finetune_pressure_end': 1.00005,
++ 'lsr_wall_strength': 0.6, # Slightly stronger wall repulsion
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+- final_radii = compute_max_radii(best_centers)
++ final_radii = compute_max_radii(best_centers, max_iter=2500, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+
+
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This uses an iterative proportional scaling method.
++def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
++ """
++ Compute maximum radii using iterative proportional scaling with a convergence check.
++ This version vectorizes distance calculations for improved performance.
+ """
+ n = centers.shape[0]
+- radii = np.ones(n)
+-
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- max_radius_iter = 600 # Maximum iterations for radius calculation
+- convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+- required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+- converged_in_a_row = 0 # Counter for consecutive converged iterations
+-
+- for iteration in range(max_radius_iter):
+- previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+-
++ if n == 0:
++ return np.array([])
++
++ # Initialize radii to be constrained by walls
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ for _ in range(max_iter):
++ old_radii = radii.copy()
++ updated = False
++
++ # Vectorized calculation for distances between all pairs
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++
++ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9: # Centers are practically identical
+- radii[i] = 0.0
+- radii[j] = 0.0
+- # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+- continue
++ dist = dists[i, j] # Use pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- # Calculate the maximum absolute change in any radius
+- max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+-
+- # Check for convergence based on max_absolute_change
+- if max_absolute_change < convergence_epsilon:
+- converged_in_a_row += 1
+- if converged_in_a_row >= required_converged_iters:
+- break # Converged for several consecutive iterations
+- else:
+- converged_in_a_row = 0 # Reset counter if change is significant
+-
+- return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
++ if dist < 1e-9: # Handle co-located centers: radii must be zero
++ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
++ radii[i], radii[j] = 0.0, 0.0
++ updated = True
++ else:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++
++ # Check for convergence: if no updates were made AND the max change is tiny.
++ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
++ break
++
++ return np.maximum(radii, 0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..59b7bedbc76bed90ac18bfa60276dbeb80962824
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/main.py
@@ -0,0 +1,315 @@
+# EVOLVE-BLOCK-START
+"""
+This solution implements a Memetic Algorithm (MA), which combines a Genetic
+Algorithm (GA) for global exploration with a powerful, physics-based local search
+for exploitation. This approach hybridizes the successful population-based
+framework of prior submissions with the refined, two-stage force-directed
+simulation from the current program, which will be repurposed as the local
+search operator.
+"""
+
+import numpy as np
+
+class LocalSearchRefiner:
+ """
+ Performs a fast, two-stage, force-directed local refinement on a set of centers.
+ This is adapted from the full simulation of the parent program to act as a
+ powerful local search operator within the Memetic Algorithm.
+ """
+ def __init__(self, config):
+ self.config = config
+ self.lsr_radius_iter = config['lsr_radius_iter']
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ n = refined_centers.shape[0]
+
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
+ progress = sim_iter / self.config['lsr_aggressive_iter']
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+
+ radii = compute_max_radii(refined_centers, max_iter=self.lsr_radius_iter, convergence_threshold=1e-7)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning with subtle growth pressure ---
+ for sim_iter_ft in range(self.config['lsr_finetune_iter']):
+ progress = sim_iter_ft / self.config['lsr_finetune_iter']
+ current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
+ current_pressure = self.config['lsr_finetune_pressure_end'] + \
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+
+ radii = compute_max_radii(refined_centers, max_iter=self.lsr_radius_iter, convergence_threshold=1e-7)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 3
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+ # Place 26th circle in a random-ish interstitial void from an expanded list
+ interstitial_points = [
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5], # Corners and center
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Near corners
+ [0.5, 0.1], [0.1, 0.5], [0.9, 0.5], [0.5, 0.9], # Midpoints of edges
+ [0.3, 0.3], [0.7, 0.7], [0.3, 0.7], [0.7, 0.3] # Inner quadrants
+ ]
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ fitnesses = []
+ # Use configurable radius iterations for population evaluation
+ for ind in self.population:
+ radii = compute_max_radii(ind, max_iter=self.config['ma_radius_iter_eval'], convergence_threshold=1e-7)
+ fitnesses.append(np.sum(radii))
+ self.fitnesses = np.array(fitnesses)
+
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over best individuals
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # --- GA Parameters ---
+ 'population_size': 100, # Increased population size for broader exploration
+ 'generations': 400, # More generations to allow for convergence
+ 'elite_count': 5, # Number of elite individuals to carry over
+ 'tournament_size': 7, # Increased tournament size for higher selection pressure
+ 'mutation_rate': 0.4, # Per-circle mutation probability, slightly higher
+ 'jump_mutation_prob': 0.04, # Increased probability of a random reset for a circle
+ 'mut_strength_start': 0.12, # Higher initial mutation strength
+ 'mut_strength_end': 0.0005, # Lower final mutation strength for fine-tuning
+ 'crossover_rate': 0.95, # Higher crossover rate
+
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.25, # Increased probability of applying local search
+
+ # --- Radius Calculation Iterations ---
+ 'ma_radius_iter_eval': 300, # Iterations for compute_max_radii during GA evaluation
+ 'lsr_radius_iter': 150, # Iterations for compute_max_radii within LocalSearchRefiner
+
+ # --- Local Search Refiner (LSR) Parameters ---
+ 'lsr_aggressive_iter': 50, # More aggressive iterations
+ 'lsr_aggressive_lr': 0.03, # Slightly higher aggressive learning rate
+ 'lsr_aggressive_pressure_start': 1.07, # Higher initial pressure
+ 'lsr_aggressive_pressure_end': 1.001,
+ 'lsr_finetune_iter': 100, # More fine-tune iterations
+ 'lsr_finetune_lr': 0.0015, # Slightly higher fine-tune learning rate
+ 'lsr_finetune_pressure_start': 1.001,
+ 'lsr_finetune_pressure_end': 1.00005,
+ 'lsr_wall_strength': 0.6, # Slightly stronger wall repulsion
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=2500, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ This version vectorizes distance calculations for improved performance.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+
+ # Vectorized calculation for distances between all pairs
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Use pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..6d95001722637c0a9df674b7dd049669085ab9e8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/original.py
@@ -0,0 +1,306 @@
+# EVOLVE-BLOCK-START
+"""
+This solution implements a Memetic Algorithm (MA), which combines a Genetic
+Algorithm (GA) for global exploration with a powerful, physics-based local search
+for exploitation. This approach hybridizes the successful population-based
+framework of prior submissions with the refined, two-stage force-directed
+simulation from the current program, which will be repurposed as the local
+search operator.
+"""
+
+import numpy as np
+
+class LocalSearchRefiner:
+ """
+ Performs a fast, two-stage, force-directed local refinement on a set of centers.
+ This is adapted from the full simulation of the parent program to act as a
+ powerful local search operator within the Memetic Algorithm.
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ n = refined_centers.shape[0]
+
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
+ progress = sim_iter / self.config['lsr_aggressive_iter']
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning with subtle growth pressure ---
+ for sim_iter_ft in range(self.config['lsr_finetune_iter']):
+ progress = sim_iter_ft / self.config['lsr_finetune_iter']
+ current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
+ current_pressure = self.config['lsr_finetune_pressure_end'] + \
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 3
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+ # Place 26th circle in a random-ish interstitial void
+ interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ fitnesses = []
+ for ind in self.population:
+ radii = compute_max_radii(ind)
+ fitnesses.append(np.sum(radii))
+ self.fitnesses = np.array(fitnesses)
+
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs blend crossover (BLX-alpha)."""
+ alpha = self.config['crossover_alpha']
+ child = alpha * p1 + (1.0 - alpha) * p2
+ return np.clip(child, 0.0, 1.0)
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over best individuals
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # --- GA Parameters ---
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 4,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.35, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'crossover_alpha': 0.5, # For BLX-alpha crossover
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ # --- Local Search Refiner (LSR) Parameters ---
+ 'lsr_aggressive_iter': 40,
+ 'lsr_aggressive_lr': 0.025,
+ 'lsr_aggressive_pressure_start': 1.06,
+ 'lsr_aggressive_pressure_end': 1.001,
+ 'lsr_finetune_iter': 80,
+ 'lsr_finetune_lr': 0.001,
+ 'lsr_finetune_pressure_start': 1.0008,
+ 'lsr_finetune_pressure_end': 1.00001,
+ 'lsr_wall_strength': 0.5,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers)
+
+ return best_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..b0813fab45f113c26116dc1c3afcb78323ee739b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results
+Run 1/1 completed in 818.43 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.3772917154386852
+ public: {'centers_str': ' centers[0] = (0.0853, 0.0839)\n centers[1] = (0.4179, 0.2613)\n centers[2] = (0.4740, 0.0598)\n centers[3] = (0.7702, 0.0769)\n centers[4] = (0.9250, 0.0747)\n centers[5] = (0.0953, 0.2657)\n centers[6] = (0.2739, 0.3487)\n centers[7] = (0.5899, 0.7190)\n centers[8] = (0.5900, 0.1928)\n centers[9] = (0.8602, 0.2788)\n centers[10] = (0.0936, 0.4567)\n centers[11] = (0.2825, 0.4973)\n centers[12] = (0.4312, 0.3691)\n centers[13] = (0.6473, 0.4598)\n centers[14] = (0.8878, 0.5300)\n centers[15] = (0.1381, 0.6855)\n centers[16] = (0.4066, 0.6833)\n centers[17] = (0.5583, 0.7715)\n centers[18] = (0.7230, 0.7103)\n centers[19] = (0.9253, 0.7136)\n centers[20] = (0.0872, 0.9100)\n centers[21] = (0.2845, 0.8887)\n centers[22] = (0.4942, 0.9033)\n centers[23] = (0.6779, 0.9163)\n centers[24] = (0.8770, 0.8904)\n centers[25] = (0.3029, 0.1512)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.3772917154386852}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/packing_viz.png
+ execution_time_mean: 818.4331594286487
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..abedcece7fc1e980959f6d4082154c3706779edd
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.3772917154386852,
+ "public": {
+ "centers_str": " centers[0] = (0.0853, 0.0839)\n centers[1] = (0.4179, 0.2613)\n centers[2] = (0.4740, 0.0598)\n centers[3] = (0.7702, 0.0769)\n centers[4] = (0.9250, 0.0747)\n centers[5] = (0.0953, 0.2657)\n centers[6] = (0.2739, 0.3487)\n centers[7] = (0.5899, 0.7190)\n centers[8] = (0.5900, 0.1928)\n centers[9] = (0.8602, 0.2788)\n centers[10] = (0.0936, 0.4567)\n centers[11] = (0.2825, 0.4973)\n centers[12] = (0.4312, 0.3691)\n centers[13] = (0.6473, 0.4598)\n centers[14] = (0.8878, 0.5300)\n centers[15] = (0.1381, 0.6855)\n centers[16] = (0.4066, 0.6833)\n centers[17] = (0.5583, 0.7715)\n centers[18] = (0.7230, 0.7103)\n centers[19] = (0.9253, 0.7136)\n centers[20] = (0.0872, 0.9100)\n centers[21] = (0.2845, 0.8887)\n centers[22] = (0.4942, 0.9033)\n centers[23] = (0.6779, 0.9163)\n centers[24] = (0.8770, 0.8904)\n centers[25] = (0.3029, 0.1512)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.3772917154386852
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/results/packing_viz.png",
+ "execution_time_mean": 818.4331594286487,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..13283f4689c5821b4311e677e92a7cee17ac63da
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_112/search_replace.txt
@@ -0,0 +1,384 @@
+The previous attempt to modify `compute_max_radii` failed because the `SEARCH` block did not precisely match the version of the function present in the code. The `compute_max_radii` function was at the end of the `EVOLVE-BLOCK-START` and had specific comments and parameter handling that needed to be matched verbatim.
+
+This updated set of edits aims to correct this and implement the remaining planned improvements.
+
+My new plan:
+1. **Correctly modify the `compute_max_radii` function at the END of the `EVOLVE-BLOCK-START` block:** Update it to accept `max_iter` and `convergence_threshold` parameters and vectorize the distance calculation, matching the exact prior successful implementation. This will significantly speed up radius computations.
+2. **Add `lsr_radius_iter` to `LocalSearchRefiner`'s configuration:** This enables the local search refiner to control the precision of radius calculations during its simulation stages.
+3. **Update `LocalSearchRefiner` calls to `compute_max_radii`:** Pass the new `lsr_radius_iter` to these calls.
+4. **Add `ma_radius_iter_eval` to `MemeticAlgorithm`'s configuration:** This parameter will control the precision of radius calculations during population evaluation.
+5. **Update `MemeticAlgorithm._evaluate_population` calls to `compute_max_radii`:** Pass the new `ma_radius_iter_eval` to these calls.
+6. **Modify `MemeticAlgorithm._crossover` to use uniform crossover:** Uniform crossover is often more effective for geometric packing problems as it preserves distinct sub-packings.
+7. **Remove `crossover_alpha` parameter:** This parameter is specific to BLX-alpha crossover and is no longer needed with uniform crossover.
+8. **Enhance `MemeticAlgorithm._initialize_population`:** Provide a more diverse set of `interstitial_points` for the 26th circle, improving initial population quality.
+9. **Update the main `construct_packing` configuration:** Integrate all new parameters (`lsr_radius_iter`, `ma_radius_iter_eval`) and fine-tune existing MA parameters for potentially better performance. Also, ensure the final `compute_max_radii` call in `construct_packing` uses high precision.
+
+---
+### **Edit 1: Refactor and Optimize `compute_max_radii`**
+This replaces the current `compute_max_radii` function at the bottom of the `EVOLVE-BLOCK-START` with an optimized version that accepts `max_iter` and `convergence_threshold` parameters and uses vectorized distance calculations.
+
+
+optimize_compute_max_radii
+
+
+
+The existing `compute_max_radii` is inefficient due to `np.linalg.norm` being called in nested loops and hardcoded iteration/convergence parameters. This edit replaces it with a faster version that:
+1. Accepts `max_iter` and `convergence_threshold` as arguments, allowing control over precision.
+2. Vectorizes pairwise distance calculations (`dists = np.sqrt(np.sum(diffs**2, axis=-1))`) once per outer iteration.
+3. Includes a robust convergence check to break early.
+
+
+
+<<<<<<< SEARCH
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+=======
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ This version vectorizes distance calculations for improved performance.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+
+ # Vectorized calculation for distances between all pairs
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Use pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+>>>>>>> REPLACE
+
+
+---
+### **Edit 2: Configure Radius Iterations for LocalSearchRefiner**
+Adds `lsr_radius_iter` to the LocalSearchRefiner's configuration for explicit control over radius calculation precision during local search, and updates the calls to `compute_max_radii` accordingly.
+
+
+lsr_radius_iter_config
+
+
+
+To allow the `LocalSearchRefiner` to tune the precision of radius calculations, `lsr_radius_iter` is added to its config and passed to `compute_max_radii` calls. This optimizes performance by using lower precision during iterative simulation and higher precision for final evaluation.
+
+
+
+<<<<<<< SEARCH
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ n = refined_centers.shape[0]
+
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
+ progress = sim_iter / self.config['lsr_aggressive_iter']
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+=======
+ self.config = config
+ self.lsr_radius_iter = config['lsr_radius_iter']
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ n = refined_centers.shape[0]
+
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
+ progress = sim_iter / self.config['lsr_aggressive_iter']
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+
+ radii = compute_max_radii(refined_centers, max_iter=self.lsr_radius_iter, convergence_threshold=1e-7)
+ inflated_radii = radii * current_pressure
+>>>>>>> REPLACE
+
+
+
+<<<<<<< SEARCH
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+=======
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+
+ radii = compute_max_radii(refined_centers, max_iter=self.lsr_radius_iter, convergence_threshold=1e-7)
+ inflated_radii = radii * current_pressure
+>>>>>>> REPLACE
+
+
+---
+### **Edit 3: Configure Radius Iterations for MemeticAlgorithm Evaluation**
+Adds `ma_radius_iter_eval` to the MemeticAlgorithm's configuration for explicit control over radius calculation precision during population evaluation.
+
+
+ma_radius_iter_eval_config
+
+
+
+To allow the `MemeticAlgorithm` to tune the precision of radius calculations during population evaluation, `ma_radius_iter_eval` is added to its config and passed to `compute_max_radii` calls. This optimizes performance by using lower precision during iterative evaluation and higher precision for the final result.
+
+
+
+<<<<<<< SEARCH
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ fitnesses = []
+ for ind in self.population:
+ radii = compute_max_radii(ind)
+ fitnesses.append(np.sum(radii))
+ self.fitnesses = np.array(fitnesses)
+=======
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ fitnesses = []
+ # Use configurable radius iterations for population evaluation
+ for ind in self.population:
+ radii = compute_max_radii(ind, max_iter=self.config['ma_radius_iter_eval'], convergence_threshold=1e-7)
+ fitnesses.append(np.sum(radii))
+ self.fitnesses = np.array(fitnesses)
+>>>>>>> REPLACE
+
+
+---
+### **Edit 4: Revert to Uniform Crossover and Remove Alpha Parameter**
+Replaces the BLX-alpha crossover with a simpler uniform crossover, which tends to work better for packing problems by preserving sub-packings. The unused `crossover_alpha` parameter is also removed.
+
+
+uniform_crossover
+
+
+
+Replaces the BLX-alpha crossover with uniform crossover (`child[mask] = p2[mask]`). Uniform crossover often performs better in combinatorial optimization by swapping entire components (circles) rather than blending their properties, which can disrupt good local arrangements. The `crossover_alpha` parameter, no longer relevant, is removed.
+
+
+
+<<<<<<< SEARCH
+ def _crossover(self, p1, p2):
+ """Performs blend crossover (BLX-alpha)."""
+ alpha = self.config['crossover_alpha']
+ child = alpha * p1 + (1.0 - alpha) * p2
+ return np.clip(child, 0.0, 1.0)
+=======
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+>>>>>>> REPLACE
+
+
+---
+### **Edit 5: Enhance Initial Population Diversity**
+Modifies the `_initialize_population` method to use a richer set of strategic starting points for the 26th circle, improving initial population quality.
+
+
+diverse_initial_pop
+
+
+
+The `_initialize_population` method is enhanced by expanding the `interstitial_points` list. This provides a wider range of strategic starting positions for the 26th circle when creating grid-based initial configurations, increasing the diversity and quality of the initial population.
+
+
+
+<<<<<<< SEARCH
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+ # Place 26th circle in a random-ish interstitial void
+ interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+=======
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+ # Place 26th circle in a random-ish interstitial void from an expanded list
+ interstitial_points = [
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5], # Corners and center
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Near corners
+ [0.5, 0.1], [0.1, 0.5], [0.9, 0.5], [0.5, 0.9], # Midpoints of edges
+ [0.3, 0.3], [0.7, 0.7], [0.3, 0.7], [0.7, 0.3] # Inner quadrants
+ ]
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+>>>>>>> REPLACE
+
+
+---
+### **Edit 6: Update `construct_packing` Configuration**
+Adjusts various parameters in the `construct_packing` function's `config` dictionary, including adding the new radius iteration parameters, removing `crossover_alpha`, and fine-tuning MA/LSR settings.
+
+
+update_ma_config
+
+
+
+The `config` dictionary in `construct_packing` is updated to integrate the newly introduced `lsr_radius_iter` and `ma_radius_iter_eval` parameters. `crossover_alpha` is removed. Other MA and LSR parameters are adjusted (e.g., population size, generations, mutation strength) to reflect best practices and encourage a balance between exploration and exploitation with the improved local search and crossover.
+
+
+
+<<<<<<< SEARCH
+ config = {
+ # --- GA Parameters ---
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 4,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.35, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'crossover_alpha': 0.5, # For BLX-alpha crossover
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ # --- Local Search Refiner (LSR) Parameters ---
+ 'lsr_aggressive_iter': 40,
+ 'lsr_aggressive_lr': 0.025,
+ 'lsr_aggressive_pressure_start': 1.06,
+ 'lsr_aggressive_pressure_end': 1.001,
+ 'lsr_finetune_iter': 80,
+ 'lsr_finetune_lr': 0.001,
+ 'lsr_finetune_pressure_start': 1.0008,
+ 'lsr_finetune_pressure_end': 1.00001,
+ 'lsr_wall_strength': 0.5,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers)
+
+ return best_centers, final_radii
+=======
+ config = {
+ # --- GA Parameters ---
+ 'population_size': 100, # Increased population size for broader exploration
+ 'generations': 400, # More generations to allow for convergence
+ 'elite_count': 5, # Number of elite individuals to carry over
+ 'tournament_size': 7, # Increased tournament size for higher selection pressure
+ 'mutation_rate': 0.4, # Per-circle mutation probability, slightly higher
+ 'jump_mutation_prob': 0.04, # Increased probability of a random reset for a circle
+ 'mut_strength_start': 0.12, # Higher initial mutation strength
+ 'mut_strength_end': 0.0005, # Lower final mutation strength for fine-tuning
+ 'crossover_rate': 0.95, # Higher crossover rate
+
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.25, # Increased probability of applying local search
+
+ # --- Radius Calculation Iterations ---
+ 'ma_radius_iter_eval': 300, # Iterations for compute_max_radii during GA evaluation
+ 'lsr_radius_iter': 150, # Iterations for compute_max_radii within LocalSearchRefiner
+
+ # --- Local Search Refiner (LSR) Parameters ---
+ 'lsr_aggressive_iter': 50, # More aggressive iterations
+ 'lsr_aggressive_lr': 0.03, # Slightly higher aggressive learning rate
+ 'lsr_aggressive_pressure_start': 1.07, # Higher initial pressure
+ 'lsr_aggressive_pressure_end': 1.001,
+ 'lsr_finetune_iter': 100, # More fine-tune iterations
+ 'lsr_finetune_lr': 0.0015, # Slightly higher fine-tune learning rate
+ 'lsr_finetune_pressure_start': 1.001,
+ 'lsr_finetune_pressure_end': 1.00005,
+ 'lsr_wall_strength': 0.6, # Slightly stronger wall repulsion
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=2500, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3d1e7869409e75536ef834edfa5170d82764c02c
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..87b0dc828c4483e2b0ab1ecd37e2f7c1ccefa1aa
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/edit.diff
@@ -0,0 +1,305 @@
+--- a/original.py
++++ b/original.py
+@@ -1,274 +1,284 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This restores the successful searcher from a previous high-scoring version.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+ class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
+ spacing = 1.0 / 5
+ candidate_extra_positions = [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ num_random_starts = self.config['population_size'] - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+- def _mutate(self, individual, strength):
++ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+- if np.random.rand() < self.config['mutation_rate']:
++ if np.random.rand() < mutation_rate_per_circle:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+- if np.random.rand() < 0.03: # Increased chance of jump mutation
++ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
++ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
++ # Anneal mutation rate per circle
++ current_mutation_rate_per_circle = self.config['mutation_rate_end_per_circle'] + \
++ (self.config['mutation_rate_start_per_circle'] - self.config['mutation_rate_end_per_circle']) * \
++ (1.0 - (gen / self.config['generations']))**1.5
++
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+- child = self._mutate(child, mut_strength)
++ child = self._mutate(child, mut_strength, current_mutation_rate_per_circle)
+
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a hybrid Memetic Algorithm.
+ """
+ n = 26
+ config = {
+- # MA parameters
+- 'population_size': 60,
+- 'generations': 250,
+- 'elite_count': 4,
+- 'tournament_size': 5,
+- 'mutation_rate': 0.4,
++ # MA parameters: Increased population, generations, and selection pressure
++ 'population_size': 80,
++ 'generations': 350,
++ 'elite_count': 5,
++ 'tournament_size': 6,
++ 'crossover_rate': 0.9,
++ 'initial_perturbation_scale': 0.02,
++
++ # Annealed mutation parameters
++ 'mutation_rate_start_per_circle': 0.45,
++ 'mutation_rate_end_per_circle': 0.05,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+- 'crossover_rate': 0.9,
+- 'initial_perturbation_scale': 0.02,
+-
+- # Memetic Parameters
++ 'jump_mutation_prob': 0.03,
++
++ # Memetic Parameters: More aggressive local search
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+- 'sim_iter': 450,
+- 'radius_sim_iter': 100,
+- 'learning_rate': 0.015,
+- 'wall_strength': 0.6,
++ 'sim_iter': 500,
++ 'radius_sim_iter': 150,
++ 'learning_rate': 0.018,
++ 'wall_strength': 0.65,
+ 'initial_growth_pressure': 1.05,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ final_radii = compute_max_radii(best_centers, max_iter=2000)
+
+ return best_centers, final_radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..3cc4399ebab5b71b084671a2885f60cd78ae05b3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/main.py
@@ -0,0 +1,284 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This restores the successful searcher from a previous high-scoring version.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
+ spacing = 1.0 / 5
+ candidate_extra_positions = [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ num_random_starts = self.config['population_size'] - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ # Anneal mutation rate per circle
+ current_mutation_rate_per_circle = self.config['mutation_rate_end_per_circle'] + \
+ (self.config['mutation_rate_start_per_circle'] - self.config['mutation_rate_end_per_circle']) * \
+ (1.0 - (gen / self.config['generations']))**1.5
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate_per_circle)
+
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a hybrid Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # MA parameters: Increased population, generations, and selection pressure
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 5,
+ 'tournament_size': 6,
+ 'crossover_rate': 0.9,
+ 'initial_perturbation_scale': 0.02,
+
+ # Annealed mutation parameters
+ 'mutation_rate_start_per_circle': 0.45,
+ 'mutation_rate_end_per_circle': 0.05,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'jump_mutation_prob': 0.03,
+
+ # Memetic Parameters: More aggressive local search
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+ 'sim_iter': 500,
+ 'radius_sim_iter': 150,
+ 'learning_rate': 0.018,
+ 'wall_strength': 0.65,
+ 'initial_growth_pressure': 1.05,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ final_radii = compute_max_radii(best_centers, max_iter=2000)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..9421b1eb90541347655c3f0542fef9845996a938
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/original.py
@@ -0,0 +1,274 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This restores the successful searcher from a previous high-scoring version.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
+ spacing = 1.0 / 5
+ candidate_extra_positions = [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ num_random_starts = self.config['population_size'] - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.03: # Increased chance of jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a hybrid Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # MA parameters
+ 'population_size': 60,
+ 'generations': 250,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.4,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'initial_perturbation_scale': 0.02,
+
+ # Memetic Parameters
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+ 'sim_iter': 450,
+ 'radius_sim_iter': 100,
+ 'learning_rate': 0.015,
+ 'wall_strength': 0.6,
+ 'initial_growth_pressure': 1.05,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ final_radii = compute_max_radii(best_centers, max_iter=2000)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..1f3aa4d2d6b7cb1f5b65936d86cf042adccc58f2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results
+Run 1/1 completed in 8952.26 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.2684012723390325
+ public: {'centers_str': ' centers[0] = (0.2667, 0.0587)\n centers[1] = (0.3858, 0.0455)\n centers[2] = (0.5140, 0.0896)\n centers[3] = (0.7936, 0.0354)\n centers[4] = (0.9073, 0.0945)\n centers[5] = (0.0579, 0.3063)\n centers[6] = (0.0996, 0.1012)\n centers[7] = (0.6256, 0.3505)\n centers[8] = (0.3570, 0.6037)\n centers[9] = (0.8784, 0.3095)\n centers[10] = (0.0810, 0.4848)\n centers[11] = (0.2059, 0.5742)\n centers[12] = (0.5060, 0.5956)\n centers[13] = (0.6808, 0.1577)\n centers[14] = (0.9234, 0.4990)\n centers[15] = (0.0765, 0.6524)\n centers[16] = (0.2344, 0.7161)\n centers[17] = (0.4035, 0.7079)\n centers[18] = (0.7052, 0.5062)\n centers[19] = (0.8881, 0.6847)\n centers[20] = (0.1136, 0.8854)\n centers[21] = (0.3484, 0.8877)\n centers[22] = (0.4746, 0.9656)\n centers[23] = (0.6317, 0.8213)\n centers[24] = (0.8999, 0.8998)\n centers[25] = (0.3518, 0.3240)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.2684012723390325}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/packing_viz.png
+ execution_time_mean: 8952.262480809819
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..8afbb048228806770c6a7574a833ef2fb32a4f57
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.2684012723390325,
+ "public": {
+ "centers_str": " centers[0] = (0.2667, 0.0587)\n centers[1] = (0.3858, 0.0455)\n centers[2] = (0.5140, 0.0896)\n centers[3] = (0.7936, 0.0354)\n centers[4] = (0.9073, 0.0945)\n centers[5] = (0.0579, 0.3063)\n centers[6] = (0.0996, 0.1012)\n centers[7] = (0.6256, 0.3505)\n centers[8] = (0.3570, 0.6037)\n centers[9] = (0.8784, 0.3095)\n centers[10] = (0.0810, 0.4848)\n centers[11] = (0.2059, 0.5742)\n centers[12] = (0.5060, 0.5956)\n centers[13] = (0.6808, 0.1577)\n centers[14] = (0.9234, 0.4990)\n centers[15] = (0.0765, 0.6524)\n centers[16] = (0.2344, 0.7161)\n centers[17] = (0.4035, 0.7079)\n centers[18] = (0.7052, 0.5062)\n centers[19] = (0.8881, 0.6847)\n centers[20] = (0.1136, 0.8854)\n centers[21] = (0.3484, 0.8877)\n centers[22] = (0.4746, 0.9656)\n centers[23] = (0.6317, 0.8213)\n centers[24] = (0.8999, 0.8998)\n centers[25] = (0.3518, 0.3240)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.2684012723390325
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/results/packing_viz.png",
+ "execution_time_mean": 8952.262480809819,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..72be0ad413f86348e5829a2dc21d31a0d9e7a3cb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_113/search_replace.txt
@@ -0,0 +1,144 @@
+
+revert_to_stronger_ma_config
+
+
+The current Memetic Algorithm (MA) configuration has a significantly lower performance (2.31) compared to a previous MA version that scored 2.39. This edit reverts the MA parameters and logic to this higher-performing configuration to recover the lost performance and push towards better results.
+
+The key changes are:
+1. **Reinstating Annealed Mutation Rate:** The mutation rate per circle is now annealed from a high value to a low value over the course of the generations. This allows for broad exploration at the start and fine-tuning at the end, replacing the current fixed mutation rate.
+2. **Tuning Hyperparameters:** The MA and local search hyperparameters (`population_size`, `generations`, `elite_count`, `tournament_size`, local search iterations, and learning rates) are adjusted to match the more aggressive and successful settings of the prior 2.39-scoring version.
+3. **Configurable Jump Mutation:** The probability of a "jump" mutation (resetting a circle's position) is made configurable via the `config` dictionary, replacing a hardcoded value.
+
+These changes collectively restore a proven-effective search strategy, increasing both the exploration capability (through a larger population and better mutation schedule) and the exploitation power (through a more intensive local search).
+
+
+<<<<<<< SEARCH
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.03: # Increased chance of jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+=======
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+=======
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ # Anneal mutation rate per circle
+ current_mutation_rate_per_circle = self.config['mutation_rate_end_per_circle'] + \
+ (self.config['mutation_rate_start_per_circle'] - self.config['mutation_rate_end_per_circle']) * \
+ (1.0 - (gen / self.config['generations']))**1.5
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate_per_circle)
+
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ config = {
+ # MA parameters
+ 'population_size': 60,
+ 'generations': 250,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.4,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'initial_perturbation_scale': 0.02,
+
+ # Memetic Parameters
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+ 'sim_iter': 450,
+ 'radius_sim_iter': 100,
+ 'learning_rate': 0.015,
+ 'wall_strength': 0.6,
+ 'initial_growth_pressure': 1.05,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+=======
+ config = {
+ # MA parameters: Increased population, generations, and selection pressure
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 5,
+ 'tournament_size': 6,
+ 'crossover_rate': 0.9,
+ 'initial_perturbation_scale': 0.02,
+
+ # Annealed mutation parameters
+ 'mutation_rate_start_per_circle': 0.45,
+ 'mutation_rate_end_per_circle': 0.05,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'jump_mutation_prob': 0.03,
+
+ # Memetic Parameters: More aggressive local search
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+ 'sim_iter': 500,
+ 'radius_sim_iter': 150,
+ 'learning_rate': 0.018,
+ 'wall_strength': 0.65,
+ 'initial_growth_pressure': 1.05,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..c80b093b0c42628d222d2b09cd2e3d1f0e867562
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/edit.diff
@@ -0,0 +1,294 @@
+--- a/original.py
++++ b/original.py
+@@ -1,254 +1,279 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+ def hybrid_local_searcher(centers, sim_iter=250, fine_tune_iter=80, radius_sim_iter=80):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.03 # Agressive but controlled pressure
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+ class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+ def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.02, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+ def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+ def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+ def mutate(individual, mutation_rate, mutation_strength, n_circles):
+- """Applies Gaussian noise to circle centers."""
++ """
++ Applies an enhanced mutation operator with multiple strategies for better exploration:
++ 1. Standard Gaussian Jitter (local fine-tuning).
++ 2. Swap Mutation (topological change).
++ 3. Re-initialize Mutation (escape deep local optima).
++ """
++ mutated_centers = individual.centers.copy()
++
++ # --- Strategy 1: Standard Gaussian Jitter ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+- individual.centers[mask] += noise[mask]
+- individual.centers = np.clip(individual.centers, 0.0, 1.0)
++ mutated_centers[mask] += noise[mask]
++
++ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
++ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
++ if n_circles > 1:
++ i, j = np.random.choice(n_circles, 2, replace=False)
++ # Swap the centers
++ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
++
++ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
++ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
++ # Pick one random circle and move it to a new random location.
++ idx_to_reinit = np.random.randint(0, n_circles)
++ mutated_centers[idx_to_reinit] = np.random.rand(2)
++
++ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+- POPULATION_SIZE = 80
+- NUM_GENERATIONS = 500
+- MUTATION_RATE = 0.20
+- INITIAL_MUTATION_STRENGTH = 0.05
+- FINAL_MUTATION_STRENGTH = 0.001
+- TOURNAMENT_SIZE = 7
+- ELITISM_COUNT = 5
+- LOCAL_SEARCH_PROB = 0.15 # Probability of applying local search to a new child
++ POPULATION_SIZE = 120 # Increased population for broader search
++ NUM_GENERATIONS = 800 # More generations for better convergence
++ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
++ INITIAL_MUTATION_STRENGTH = 0.08 # Larger initial mutations for exploration
++ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
++ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
++ ELITISM_COUNT = 8 # Preserve more of the best solutions
++ LOCAL_SEARCH_PROB = 0.20 # Increased probability of applying local search
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+- # Initialize population by evaluating fitness (no premature local search)
++ # --- Crucial Memetic Step: Initial Local Optimization ---
++ # Apply local search to the entire initial population to create a strong starting gene pool
++ # of locally-optimal individuals. This is a key feature of successful memetic algorithms.
+ for individual in population:
++ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Copy to prevent modification of the best individual if it is an elite
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+
+ # Final polish on the best solution found with an intensive local search
+ best_individual_overall.centers = hybrid_local_searcher(best_individual_overall.centers.copy(), sim_iter=600, fine_tune_iter=250, radius_sim_iter=200)
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..6973c37fd973c5cfea48424c41e3caa021d43260
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/main.py
@@ -0,0 +1,279 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=250, fine_tune_iter=80, radius_sim_iter=80):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.03 # Agressive but controlled pressure
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.02, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies for better exploration:
+ 1. Standard Gaussian Jitter (local fine-tuning).
+ 2. Swap Mutation (topological change).
+ 3. Re-initialize Mutation (escape deep local optima).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ mutated_centers[mask] += noise[mask]
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ # Swap the centers
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 120 # Increased population for broader search
+ NUM_GENERATIONS = 800 # More generations for better convergence
+ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.08 # Larger initial mutations for exploration
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Preserve more of the best solutions
+ LOCAL_SEARCH_PROB = 0.20 # Increased probability of applying local search
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # --- Crucial Memetic Step: Initial Local Optimization ---
+ # Apply local search to the entire initial population to create a strong starting gene pool
+ # of locally-optimal individuals. This is a key feature of successful memetic algorithms.
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Copy to prevent modification of the best individual if it is an elite
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+
+ # Final polish on the best solution found with an intensive local search
+ best_individual_overall.centers = hybrid_local_searcher(best_individual_overall.centers.copy(), sim_iter=600, fine_tune_iter=250, radius_sim_iter=200)
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c50bf31d25edf12b92b3208b810fdd15fa96184
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/original.py
@@ -0,0 +1,254 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=250, fine_tune_iter=80, radius_sim_iter=80):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.03 # Agressive but controlled pressure
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.02, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 500
+ MUTATION_RATE = 0.20
+ INITIAL_MUTATION_STRENGTH = 0.05
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7
+ ELITISM_COUNT = 5
+ LOCAL_SEARCH_PROB = 0.15 # Probability of applying local search to a new child
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize population by evaluating fitness (no premature local search)
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Copy to prevent modification of the best individual if it is an elite
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+
+ # Final polish on the best solution found with an intensive local search
+ best_individual_overall.centers = hybrid_local_searcher(best_individual_overall.centers.copy(), sim_iter=600, fine_tune_iter=250, radius_sim_iter=200)
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..250dabe4d405e52d7c0063c8329f11bbad164bd4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_114/search_replace.txt
@@ -0,0 +1,107 @@
+An expert analysis of the current Memetic Algorithm (MA) and its comparison to higher-scoring prior programs reveals several opportunities for significant improvement. The current implementation is a "light" MA, which can be upgraded to a more powerful, "heavy" MA by fully embracing the principles that made prior versions successful.
+
+The core idea is to enhance both the global exploration capabilities of the Genetic Algorithm and the local refinement power of the force-directed search, while also providing a better starting point and more time for the search to converge.
+
+The proposed changes are as follows:
+1. **Full Memetic Initialization:** A crucial feature of high-performing MAs is to start with a population of locally optimal solutions. The current algorithm initializes with random and grid-based configurations but does not refine them. By applying the `hybrid_local_searcher` to every individual in the initial population, we create a much stronger "gene pool" from the outset, allowing the crossover operator to combine features of already-good solutions.
+
+2. **Enhanced Mutation Operator:** The current mutation operator only applies Gaussian jitter, which is effective for local fine-tuning but poor for escaping deep local optima or exploring different packing topologies. We will replace it with a multi-strategy operator, inspired by successful prior programs, that includes:
+ * **Swap Mutation:** Probabilistically swaps the positions of two circles, enabling significant topological changes.
+ * **Re-initialize Mutation:** Moves a single, potentially "stuck," circle to a new random location, providing a powerful escape mechanism.
+
+3. **Aggressive Hyperparameter Tuning:** The search budget and operator probabilities are increased, based on the parameters of the most successful prior programs. Increasing the population size, number of generations, initial mutation strength, and the probability of applying local search gives the algorithm more resources and a stronger bias towards finding and refining high-quality solutions.
+
+These changes synergize to create a more robust and powerful optimization process, better equipped to navigate the complex landscape of the circle packing problem for N=26.
+
+
+full_memetic_upgrade
+
+
+This set of changes transforms the current program into a more powerful Memetic Algorithm by incorporating three key strategies from higher-performing prior versions:
+
+1. **Full Memetic Initialization**: Applies the `hybrid_local_searcher` to every individual in the initial population. This starts the genetic algorithm with a strong gene pool of locally-optimal solutions, a cornerstone of effective MAs.
+
+2. **Enhanced Multi-Strategy Mutation**: Replaces the simple Gaussian jitter mutation with a more sophisticated operator that includes probabilistic **Swap Mutation** (for topological changes) and **Re-initialize Mutation** (to escape deep local optima), significantly improving the algorithm's exploration capabilities.
+
+3. **Aggressive Hyperparameter Tuning**: Increases `POPULATION_SIZE` (80->120), `NUM_GENERATIONS` (500->800), `INITIAL_MUTATION_STRENGTH` (0.05->0.08), `ELITISM_COUNT` (5->8), and `LOCAL_SEARCH_PROB` (0.15->0.20) to provide a more extensive and intensive search, mirroring the configurations of successful prior programs.
+
+This holistic upgrade aims to improve both exploration and exploitation, driving the search towards higher-quality packing configurations.
+
+
+<<<<<<< SEARCH
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+=======
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies for better exploration:
+ 1. Standard Gaussian Jitter (local fine-tuning).
+ 2. Swap Mutation (topological change).
+ 3. Re-initialize Mutation (escape deep local optima).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ mutated_centers[mask] += noise[mask]
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ # Swap the centers
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 500
+ MUTATION_RATE = 0.20
+ INITIAL_MUTATION_STRENGTH = 0.05
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7
+ ELITISM_COUNT = 5
+ LOCAL_SEARCH_PROB = 0.15 # Probability of applying local search to a new child
+=======
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 120 # Increased population for broader search
+ NUM_GENERATIONS = 800 # More generations for better convergence
+ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.08 # Larger initial mutations for exploration
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Preserve more of the best solutions
+ LOCAL_SEARCH_PROB = 0.20 # Increased probability of applying local search
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ # Initialize population by evaluating fitness (no premature local search)
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+=======
+ # --- Crucial Memetic Step: Initial Local Optimization ---
+ # Apply local search to the entire initial population to create a strong starting gene pool
+ # of locally-optimal individuals. This is a key feature of successful memetic algorithms.
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3f95e94668275cd57c44d4020b8240335df0020a
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..491ab423853e101f30beb815acdf46306f9acc2f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/edit.diff
@@ -0,0 +1,336 @@
+--- a/original.py
++++ b/original.py
+@@ -1,256 +1,306 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+-def hybrid_local_searcher(centers, sim_iter=200, fine_tune_iter=70, radius_sim_iter=70):
++def hybrid_local_searcher(centers, sim_iter=300, fine_tune_iter=100, radius_sim_iter=100):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for robust local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+ class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+ def initialize_population(n_circles, population_size):
+- """Initializes a diverse population including grid-based and random individuals."""
++ """
++ Initializes a diverse population including grid-based and random individuals.
++ Enhanced with multiple structured grid patterns for broader topological starting points.
++ """
+ population = []
+- # 1. Add a known good starting grid (5x5 + 1)
++
++ # 1. Add a known good starting grid (5x5 + 1 interstitial circle)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+- grid_centers = np.zeros((n_circles, 2))
++ grid_centers_5x5 = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+- grid_centers[k, 0] = (i + 0.5) * spacing
+- grid_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+- grid_centers[n_circles - 1] = [spacing, spacing]
+- population.append(Individual(grid_centers, n_circles))
+-
+- # 2. Add perturbed versions of the grid
+- for _ in range(population_size // 5):
+- perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
++ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
++ grid_centers_5x5[k, 0] = (i + 0.5) * spacing
++ grid_centers_5x5[k, 1] = (j + 0.5) * spacing
++ k += 1
++ else:
++ break
++ # Place the 26th circle at a common interstitial point
++ if k < n_circles:
++ grid_centers_5x5[k] = [spacing, spacing]
++
++ # Add the base grid configuration and some perturbed versions
++ population.append(Individual(grid_centers_5x5.copy(), n_circles))
++ perturbed_grid_count = population_size // 10
++ perturbation_strength_grid = 0.02
++ for _ in range(perturbed_grid_count):
++ perturbed_centers = grid_centers_5x5 + np.random.normal(0, perturbation_strength_grid, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+- # 3. Fill with random individuals for diversity
++ # 2. Add configurations based on a 6x5 grid structure for additional topological diversity
++ # This provides a different structural baseline that might be optimal for certain N values.
++ if n_circles <= 30: # Max for 6x5 is 30.
++ num_6x5_configs = population_size // 8 # Add a few of these
++ nx, ny = 6, 5
++ spacing_x = 1.0 / nx
++ spacing_y = 1.0 / ny
++ grid_6x5_base = np.zeros((nx * ny, 2))
++ k_6x5 = 0
++ for j_6x5 in range(ny):
++ for i_6x5 in range(nx):
++ grid_6x5_base[k_6x5, 0] = (i_6x5 + 0.5) * spacing_x
++ grid_6x5_base[k_6x5, 1] = (j_6x5 + 0.5) * spacing_y
++ k_6x5 += 1
++
++ for _ in range(num_6x5_configs):
++ config_6x5 = grid_6x5_base[:n_circles].copy()
++ config_6x5 += np.random.normal(0, 0.02, (n_circles, 2)) # Perturb slightly
++ population.append(Individual(np.clip(config_6x5, 0.0, 1.0), n_circles))
++
++
++ # 3. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+ def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+ def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+ def mutate(individual, mutation_rate, mutation_strength, n_circles):
+- """Applies Gaussian noise to circle centers."""
++ """
++ Applies enhanced mutation strategies: Gaussian noise, swap, and re-initialize.
++ """
++ mutated_centers = individual.centers.copy()
++
++ # Strategy 1: Standard Gaussian Jitter (applied to a subset of circles)
+ mask = np.random.rand(n_circles) < mutation_rate
+- noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+- individual.centers[mask] += noise[mask]
+- individual.centers = np.clip(individual.centers, 0.0, 1.0)
++ if np.any(mask):
++ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
++
++ # Strategy 2: Swap Mutation (applied probabilistically)
++ if np.random.rand() < 0.10: # 10% chance to swap two random circles
++ if n_circles > 1:
++ i, j = np.random.choice(n_circles, 2, replace=False)
++ mutated_centers[[i, j]] = mutated_centers[[j, i]]
++
++ # Strategy 3: Re-initialize Mutation (applied probabilistically)
++ if np.random.rand() < 0.05: # 5% chance to re-initialize one random circle
++ idx_to_reinit = np.random.randint(0, n_circles)
++ mutated_centers[idx_to_reinit] = np.random.rand(2)
++
++ individual.centers = np.clip(mutated_centers, 0.0, 1.0)
+ return individual
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+- POPULATION_SIZE = 100 # Increased population for broader search
+- NUM_GENERATIONS = 600 # Increased generations due to faster evaluations
+- MUTATION_RATE = 0.15
+- INITIAL_MUTATION_STRENGTH = 0.06
++ POPULATION_SIZE = 120 # Increased population for broader search
++ NUM_GENERATIONS = 800 # Increased generations for more thorough search
++ MUTATION_RATE = 0.18 # Adjusted mutation rate
++ INITIAL_MUTATION_STRENGTH = 0.08 # Increased initial mutation strength
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7 # Increased tournament size for stronger selection pressure
+- ELITISM_COUNT = 6 # Increased elite count to preserve more good solutions
+- LOCAL_SEARCH_PROB = 0.25 # Reduced probability for local search to balance exploration/exploitation
+-
+- GA_RADIUS_ITER = 70 # Faster radius evaluation for GA loop
+- FINAL_RADIUS_ITER = 500 # High-precision for final best solution
++ ELITISM_COUNT = 8 # Increased elite count to preserve more good solutions
++ LOCAL_SEARCH_PROB = 0.20 # Balanced probability for local search
++
++ GA_RADIUS_ITER = 100 # More accurate radius evaluation for GA loop
++ FINAL_RADIUS_ITER = 600 # High-precision for final best solution
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
++ # This crucial step provides strong, locally optimized starting points.
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+ # Final evaluation of the best individual with high precision
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..72c65a9a338445e19ed6b65d798caf5c0daa100e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/main.py
@@ -0,0 +1,306 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=300, fine_tune_iter=100, radius_sim_iter=100):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for robust local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population including grid-based and random individuals.
+ Enhanced with multiple structured grid patterns for broader topological starting points.
+ """
+ population = []
+
+ # 1. Add a known good starting grid (5x5 + 1 interstitial circle)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_5x5 = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_5x5[k, 0] = (i + 0.5) * spacing
+ grid_centers_5x5[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles:
+ grid_centers_5x5[k] = [spacing, spacing]
+
+ # Add the base grid configuration and some perturbed versions
+ population.append(Individual(grid_centers_5x5.copy(), n_circles))
+ perturbed_grid_count = population_size // 10
+ perturbation_strength_grid = 0.02
+ for _ in range(perturbed_grid_count):
+ perturbed_centers = grid_centers_5x5 + np.random.normal(0, perturbation_strength_grid, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Add configurations based on a 6x5 grid structure for additional topological diversity
+ # This provides a different structural baseline that might be optimal for certain N values.
+ if n_circles <= 30: # Max for 6x5 is 30.
+ num_6x5_configs = population_size // 8 # Add a few of these
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5_base = np.zeros((nx * ny, 2))
+ k_6x5 = 0
+ for j_6x5 in range(ny):
+ for i_6x5 in range(nx):
+ grid_6x5_base[k_6x5, 0] = (i_6x5 + 0.5) * spacing_x
+ grid_6x5_base[k_6x5, 1] = (j_6x5 + 0.5) * spacing_y
+ k_6x5 += 1
+
+ for _ in range(num_6x5_configs):
+ config_6x5 = grid_6x5_base[:n_circles].copy()
+ config_6x5 += np.random.normal(0, 0.02, (n_circles, 2)) # Perturb slightly
+ population.append(Individual(np.clip(config_6x5, 0.0, 1.0), n_circles))
+
+
+ # 3. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies enhanced mutation strategies: Gaussian noise, swap, and re-initialize.
+ """
+ mutated_centers = individual.centers.copy()
+
+ # Strategy 1: Standard Gaussian Jitter (applied to a subset of circles)
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # Strategy 2: Swap Mutation (applied probabilistically)
+ if np.random.rand() < 0.10: # 10% chance to swap two random circles
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ mutated_centers[[i, j]] = mutated_centers[[j, i]]
+
+ # Strategy 3: Re-initialize Mutation (applied probabilistically)
+ if np.random.rand() < 0.05: # 5% chance to re-initialize one random circle
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 120 # Increased population for broader search
+ NUM_GENERATIONS = 800 # Increased generations for more thorough search
+ MUTATION_RATE = 0.18 # Adjusted mutation rate
+ INITIAL_MUTATION_STRENGTH = 0.08 # Increased initial mutation strength
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7 # Increased tournament size for stronger selection pressure
+ ELITISM_COUNT = 8 # Increased elite count to preserve more good solutions
+ LOCAL_SEARCH_PROB = 0.20 # Balanced probability for local search
+
+ GA_RADIUS_ITER = 100 # More accurate radius evaluation for GA loop
+ FINAL_RADIUS_ITER = 600 # High-precision for final best solution
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ # This crucial step provides strong, locally optimized starting points.
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+ # Final evaluation of the best individual with high precision
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..6868f07628cb6a6d9381e1a412c20756dd87bda8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/original.py
@@ -0,0 +1,256 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=200, fine_tune_iter=70, radius_sim_iter=70):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for robust local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 100 # Increased population for broader search
+ NUM_GENERATIONS = 600 # Increased generations due to faster evaluations
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7 # Increased tournament size for stronger selection pressure
+ ELITISM_COUNT = 6 # Increased elite count to preserve more good solutions
+ LOCAL_SEARCH_PROB = 0.25 # Reduced probability for local search to balance exploration/exploitation
+
+ GA_RADIUS_ITER = 70 # Faster radius evaluation for GA loop
+ FINAL_RADIUS_ITER = 500 # High-precision for final best solution
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+ # Final evaluation of the best individual with high precision
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..ae6a23beb5f7365e706bbf292e6389eceb014ade
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results
+Run 1/1 completed in 18936.97 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.429306855688043
+ public: {'centers_str': ' centers[0] = (0.0809, 0.0825)\n centers[1] = (0.6113, 0.6155)\n centers[2] = (0.4555, 0.1708)\n centers[3] = (0.6226, 0.0567)\n centers[4] = (0.8559, 0.0976)\n centers[5] = (0.1109, 0.2813)\n centers[6] = (0.3856, 0.7568)\n centers[7] = (0.9660, 0.1701)\n centers[8] = (0.6708, 0.2463)\n centers[9] = (0.9044, 0.2842)\n centers[10] = (0.1138, 0.5068)\n centers[11] = (0.3711, 0.3888)\n centers[12] = (0.2748, 0.6534)\n centers[13] = (0.0967, 0.7466)\n centers[14] = (0.8660, 0.5115)\n centers[15] = (0.2721, 0.1184)\n centers[16] = (0.6283, 0.4866)\n centers[17] = (0.4835, 0.6425)\n centers[18] = (0.6984, 0.6944)\n centers[19] = (0.9273, 0.9255)\n centers[20] = (0.0786, 0.9214)\n centers[21] = (0.2742, 0.8778)\n centers[22] = (0.5303, 0.8722)\n centers[23] = (0.9013, 0.7563)\n centers[24] = (0.7559, 0.9010)\n centers[25] = (0.4342, 0.0539)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.429306855688043}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/packing_viz.png
+ execution_time_mean: 18936.969764539972
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..fbc67b90a7715579010514fa9251dcfe51e02cc1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.429306855688043,
+ "public": {
+ "centers_str": " centers[0] = (0.0809, 0.0825)\n centers[1] = (0.6113, 0.6155)\n centers[2] = (0.4555, 0.1708)\n centers[3] = (0.6226, 0.0567)\n centers[4] = (0.8559, 0.0976)\n centers[5] = (0.1109, 0.2813)\n centers[6] = (0.3856, 0.7568)\n centers[7] = (0.9660, 0.1701)\n centers[8] = (0.6708, 0.2463)\n centers[9] = (0.9044, 0.2842)\n centers[10] = (0.1138, 0.5068)\n centers[11] = (0.3711, 0.3888)\n centers[12] = (0.2748, 0.6534)\n centers[13] = (0.0967, 0.7466)\n centers[14] = (0.8660, 0.5115)\n centers[15] = (0.2721, 0.1184)\n centers[16] = (0.6283, 0.4866)\n centers[17] = (0.4835, 0.6425)\n centers[18] = (0.6984, 0.6944)\n centers[19] = (0.9273, 0.9255)\n centers[20] = (0.0786, 0.9214)\n centers[21] = (0.2742, 0.8778)\n centers[22] = (0.5303, 0.8722)\n centers[23] = (0.9013, 0.7563)\n centers[24] = (0.7559, 0.9010)\n centers[25] = (0.4342, 0.0539)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.429306855688043
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/results/packing_viz.png",
+ "execution_time_mean": 18936.969764539972,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5d7c14b643a16b9ef0671d01f751452811cc78a5
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_115/search_replace.txt
@@ -0,0 +1,482 @@
+The current program uses a Memetic Algorithm (MA) structure, combining a Genetic Algorithm (GA) with a `HybridLocalSearcher`. While it performs an initial local optimization on the entire population (a strong feature), its `mutate` operator is basic (only Gaussian noise) and its initial population diversity could be further improved. Comparing with previous higher-performing MA programs (e.g., those achieving 2.37 and 2.34), several key enhancements are identified:
+
+1. **Enriching the `mutate` operator**: Previous successful MA implementations included not just Gaussian jitter but also swap mutation (to change the relative positions of two circles) and re-initialization mutation (to completely re-randomize a circle's position). These operations are vital for allowing the GA to make larger, topological changes to the packing configuration, helping to escape local optima that simple jitter cannot.
+
+2. **Increasing initial population diversity**: While a `5x5 grid + 1` configuration is a good starting point, adding another structured grid (like a `6x5` grid, as seen in some successful runs for 26 circles) provides additional topological diversity in the initial population. This gives the MA more varied "templates" to begin its search, which is crucial for complex packing problems.
+
+3. **Adjusting hyperparameters for increased exploration and refinement**: A slightly larger `POPULATION_SIZE` and `NUM_GENERATIONS` allow the enhanced GA more opportunities to explore and exploit the solution space. Additionally, aligning the `hybrid_local_searcher` and `GA_RADIUS_ITER` parameters with those that yielded a 2.37 score ensures a more balanced and effective interplay between the GA's global search and the local searcher's refinement capabilities. Specifically, increasing the local search iterations helps in more thorough refinement within each application.
+
+These changes are aimed at improving the exploration capabilities of the GA and providing more robust local optimization, which are critical for increasing the sum of radii.
+
+
+enhance_mutation_and_initialization
+
+
+
+This edit introduces two significant improvements:
+1. **Enhanced Mutation Operator**: The `mutate` function is upgraded to include `swap_mutation` and `re_initialize_mutation` in addition to the existing Gaussian jitter. This allows the Genetic Algorithm to make more substantial topological changes to circle arrangements, which is crucial for escaping local optima and exploring a wider range of solutions. The probabilities for these new mutation types are set to encourage broader exploration.
+2. **Diversified Population Initialization**: The `initialize_population` function now includes configurations based on a `6x5` grid structure, in addition to the existing `5x5 + 1` grid. This provides the Genetic Algorithm with a more diverse set of initial solutions, offering different geometric starting patterns and potentially leading to better global optima.
+
+Additionally, several hyperparameters for the Memetic Algorithm and the `HybridLocalSearcher` are tuned for better performance, based on insights from prior high-performing runs:
+- `hybrid_local_searcher` parameters (`sim_iter`, `fine_tune_iter`, `radius_sim_iter`) are increased for more thorough local optimization.
+- `GA_RADIUS_ITER` is increased for more accurate fitness evaluation during the GA loop.
+- `POPULATION_SIZE` and `NUM_GENERATIONS` are increased to allow for more extensive search given the enhanced operators.
+- `LOCAL_SEARCH_PROB` is slightly reduced from 0.25 to 0.20 to balance exploitation with the new, more powerful mutation operators.
+- `INITIAL_MUTATION_STRENGTH` is slightly increased to encourage more aggressive initial exploration.
+
+
+
+<<<<<<< SEARCH
+def hybrid_local_searcher(centers, sim_iter=200, fine_tune_iter=70, radius_sim_iter=70):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for robust local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 100 # Increased population for broader search
+ NUM_GENERATIONS = 600 # Increased generations due to faster evaluations
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7 # Increased tournament size for stronger selection pressure
+ ELITISM_COUNT = 6 # Increased elite count to preserve more good solutions
+ LOCAL_SEARCH_PROB = 0.25 # Reduced probability for local search to balance exploration/exploitation
+
+ GA_RADIUS_ITER = 70 # Faster radius evaluation for GA loop
+ FINAL_RADIUS_ITER = 500 # High-precision for final best solution
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+ # Final evaluation of the best individual with high precision
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+=======
+def hybrid_local_searcher(centers, sim_iter=300, fine_tune_iter=100, radius_sim_iter=100):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for robust local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population including grid-based and random individuals.
+ Enhanced with multiple structured grid patterns for broader topological starting points.
+ """
+ population = []
+
+ # 1. Add a known good starting grid (5x5 + 1 interstitial circle)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_5x5 = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_5x5[k, 0] = (i + 0.5) * spacing
+ grid_centers_5x5[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles:
+ grid_centers_5x5[k] = [spacing, spacing]
+
+ # Add the base grid configuration and some perturbed versions
+ population.append(Individual(grid_centers_5x5.copy(), n_circles))
+ perturbed_grid_count = population_size // 10
+ perturbation_strength_grid = 0.02
+ for _ in range(perturbed_grid_count):
+ perturbed_centers = grid_centers_5x5 + np.random.normal(0, perturbation_strength_grid, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Add configurations based on a 6x5 grid structure for additional topological diversity
+ # This provides a different structural baseline that might be optimal for certain N values.
+ if n_circles <= 30: # Max for 6x5 is 30.
+ num_6x5_configs = population_size // 8 # Add a few of these
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5_base = np.zeros((nx * ny, 2))
+ k_6x5 = 0
+ for j_6x5 in range(ny):
+ for i_6x5 in range(nx):
+ grid_6x5_base[k_6x5, 0] = (i_6x5 + 0.5) * spacing_x
+ grid_6x5_base[k_6x5, 1] = (j_6x5 + 0.5) * spacing_y
+ k_6x5 += 1
+
+ for _ in range(num_6x5_configs):
+ config_6x5 = grid_6x5_base[:n_circles].copy()
+ config_6x5 += np.random.normal(0, 0.02, (n_circles, 2)) # Perturb slightly
+ population.append(Individual(np.clip(config_6x5, 0.0, 1.0), n_circles))
+
+
+ # 3. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies enhanced mutation strategies: Gaussian noise, swap, and re-initialize.
+ """
+ mutated_centers = individual.centers.copy()
+
+ # Strategy 1: Standard Gaussian Jitter (applied to a subset of circles)
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # Strategy 2: Swap Mutation (applied probabilistically)
+ if np.random.rand() < 0.10: # 10% chance to swap two random circles
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ mutated_centers[[i, j]] = mutated_centers[[j, i]]
+
+ # Strategy 3: Re-initialize Mutation (applied probabilistically)
+ if np.random.rand() < 0.05: # 5% chance to re-initialize one random circle
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 120 # Increased population for broader search
+ NUM_GENERATIONS = 800 # Increased generations for more thorough search
+ MUTATION_RATE = 0.18 # Adjusted mutation rate
+ INITIAL_MUTATION_STRENGTH = 0.08 # Increased initial mutation strength
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7 # Increased tournament size for stronger selection pressure
+ ELITISM_COUNT = 8 # Increased elite count to preserve more good solutions
+ LOCAL_SEARCH_PROB = 0.20 # Balanced probability for local search
+
+ GA_RADIUS_ITER = 100 # More accurate radius evaluation for GA loop
+ FINAL_RADIUS_ITER = 600 # High-precision for final best solution
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ # This crucial step provides strong, locally optimized starting points.
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+ # Final evaluation of the best individual with high precision
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..d4e53ee9c0a0da7d27648f50138c0da5944b6b3f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/edit.diff
@@ -0,0 +1,378 @@
+--- a/original.py
++++ b/original.py
+@@ -1,256 +1,323 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+-def compute_max_radii(centers, iterations=500):
++def compute_max_radii(centers, max_iter=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+- for _ in range(iterations):
++ for _ in range(max_iter): # Renamed iterations to max_iter
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+-def hybrid_local_searcher(centers, sim_iter=200, fine_tune_iter=70, radius_sim_iter=70):
++def hybrid_local_searcher(centers, sim_iter, fine_tune_iter, radius_sim_iter,
++ learning_rate, wall_strength, initial_growth_pressure,
++ final_growth_pressure, fine_tune_lr):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
++ Parameters are now passed explicitly for better configurability.
+ """
+ n = centers.shape[0]
+-
+- # Tuned hyperparameters for robust local optimization
+- learning_rate = 0.02
+- wall_strength = 0.8
+- initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+- final_growth_pressure = 1.001
+- fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+- current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
++ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter) # Use max_iter
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+- current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
++ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter) # Use max_iter
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+ class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+ def initialize_population(n_circles, population_size):
+- """Initializes a diverse population including grid-based and random individuals."""
++ """
++ Initializes a diverse population including grid-based and random individuals,
++ with an emphasis on structured initial placements for the 26th circle.
++ """
+ population = []
+- # 1. Add a known good starting grid (5x5 + 1)
++
++ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+- grid_centers = np.zeros((n_circles, 2))
++ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+- grid_centers[k, 0] = (i + 0.5) * spacing
+- grid_centers[k, 1] = (j + 0.5) * spacing
++ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
++ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+- grid_centers[n_circles - 1] = [spacing, spacing]
+- population.append(Individual(grid_centers, n_circles))
+-
+- # 2. Add perturbed versions of the grid
+- for _ in range(population_size // 5):
+- perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+- population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+-
+- # 3. Fill with random individuals for diversity
++
++ # Strategic candidate positions for the 26th circle, covering corners, edges, and interstitial spaces.
++ extra_pos_candidates = [
++ [spacing, spacing], # (0.2, 0.2)
++ [spacing, 0.5], # (0.2, 0.5)
++ [spacing, 1 - spacing], # (0.2, 0.8)
++ [0.5, spacing], # (0.5, 0.2)
++ [0.5, 0.5], # (0.5, 0.5) - Center
++ [0.5, 1 - spacing], # (0.5, 0.8)
++ [1 - spacing, spacing], # (0.8, 0.2)
++ [1 - spacing, 0.5], # (0.8, 0.5)
++ [1 - spacing, 1 - spacing], # (0.8, 0.8)
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
++ [0.05, 0.5], [0.5, 0.05], [0.95, 0.5], [0.5, 0.95], # Edge midpoints
++ [0.25, 0.25], [0.75, 0.75], # Diagonal
++ [0.25, 0.75], [0.75, 0.25],
++ [0.3, 0.1], [0.7, 0.1], # more points slightly off grid
++ [0.1, 0.3], [0.1, 0.7]
++ ]
++
++ # Generate initial individuals from these grid configurations with slight perturbations
++ num_grid_starts_to_generate = min(population_size // 2, len(extra_pos_candidates) * 2) # Up to half population from structured starts
++ perturbation_strength_initial_grid = 0.015
++
++ for i in range(num_grid_starts_to_generate):
++ current_centers = np.zeros((n_circles, 2))
++ current_centers[:n_circles-1] = base_grid_centers_25.copy()
++
++ # Select an extra position candidate, cycling through them
++ extra_pos_idx = i % len(extra_pos_candidates)
++ extra_pos = extra_pos_candidates[extra_pos_idx]
++ current_centers[n_circles-1] = np.array(extra_pos)
++
++ # Apply a small perturbation to all circles in this configuration
++ current_centers += np.random.normal(0, perturbation_strength_initial_grid, (n_circles, 2))
++
++ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
++
++ # Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+- population.append(Individual(np.random.rand(n_circles, 2), n_circles))
++ random_centers = np.random.rand(n_circles, 2)
++ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+ def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+- p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+- while True:
+- p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+- if p2 is not p1: return p1, p2
++ # Select parent 1 by picking the fittest from a random tournament subset
++ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
++ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
++
++ # Select parent 2, ensuring it's different from parent 1
++ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
++ if len(tournament_p2_candidates) > 0:
++ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
++ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
++ parent2 = np.random.choice(population)
++ while parent2 is parent1: # Ensure parent2 is definitely different
++ parent2 = np.random.choice(population)
++ return parent1, parent2
+
+ def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+- return Individual(child_centers, n_circles)
++ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+ def mutate(individual, mutation_rate, mutation_strength, n_circles):
+- """Applies Gaussian noise to circle centers."""
+- mask = np.random.rand(n_circles) < mutation_rate
+- noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+- individual.centers[mask] += noise[mask]
+- individual.centers = np.clip(individual.centers, 0.0, 1.0)
++ """
++ Applies Gaussian noise mutation to individual circle centers.
++ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
++ perturbed by Gaussian noise with `mutation_strength` standard deviation.
++ """
++ mutated_centers = individual.centers.copy()
++ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
++
++ # Apply Gaussian noise only to selected circles
++ if np.any(mask):
++ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
++
++ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
++ This version incorporates enhanced initial population diversity and
++ differentiated local search parameters for better performance.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+- POPULATION_SIZE = 100 # Increased population for broader search
+- NUM_GENERATIONS = 600 # Increased generations due to faster evaluations
+- MUTATION_RATE = 0.15
+- INITIAL_MUTATION_STRENGTH = 0.06
+- FINAL_MUTATION_STRENGTH = 0.001
+- TOURNAMENT_SIZE = 7 # Increased tournament size for stronger selection pressure
+- ELITISM_COUNT = 6 # Increased elite count to preserve more good solutions
+- LOCAL_SEARCH_PROB = 0.25 # Reduced probability for local search to balance exploration/exploitation
+-
+- GA_RADIUS_ITER = 70 # Faster radius evaluation for GA loop
+- FINAL_RADIUS_ITER = 500 # High-precision for final best solution
++ POPULATION_SIZE = 120 # Increased population for broader search
++ NUM_GENERATIONS = 800 # Increased generations for longer evolution
++ MUTATION_RATE = 0.18 # Balanced mutation rate
++ INITIAL_MUTATION_STRENGTH = 0.07 # Initial std dev for Gaussian mutation noise
++ FINAL_MUTATION_STRENGTH = 0.001 # Final std dev (annealed)
++ TOURNAMENT_SIZE = 6 # Tournament size for stronger selection pressure
++ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
++ LOCAL_SEARCH_PROB = 0.15 # Prob. to apply local search to a new child (reduced slightly for more GA exploration)
++
++ GA_RADIUS_ITER = 100 # Faster radius evaluation for GA loop
++ FINAL_RADIUS_ITER = 600 # High-precision for final best solution
++
++ # Local Search parameters for within the GA loop (lighter, faster iterations)
++ ga_local_search_params = {
++ 'sim_iter': 200, 'fine_tune_iter': 70, 'radius_sim_iter': 80,
++ 'learning_rate': 0.02, 'wall_strength': 0.8,
++ 'initial_growth_pressure': 1.04, 'final_growth_pressure': 1.001,
++ 'fine_tune_lr': 0.001
++ }
++
++ # Local Search parameters for the final polish (more intensive, run once)
++ final_local_search_params = {
++ 'sim_iter': 800, 'fine_tune_iter': 250, 'radius_sim_iter': 200,
++ 'learning_rate': 0.02, 'wall_strength': 0.85, # Slightly stronger wall repulsion
++ 'initial_growth_pressure': 1.05, 'final_growth_pressure': 1.001,
++ 'fine_tune_lr': 0.0005 # Even smaller learning rate for precision
++ }
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+- # Initialize and optimize the entire starting population
++ # Initialize and optimize the entire starting population with light local search
+ for individual in population:
+- individual.centers = hybrid_local_searcher(individual.centers.copy())
++ individual.centers = hybrid_local_searcher(individual.centers.copy(), **ga_local_search_params)
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+- child.centers = hybrid_local_searcher(child.centers.copy())
++ child.centers = hybrid_local_searcher(child.centers.copy(), **ga_local_search_params)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+- # Final evaluation of the best individual with high precision
++ # Final intensive local search and evaluation of the best individual
++ best_individual_overall.centers = hybrid_local_searcher(best_individual_overall.centers.copy(), **final_local_search_params)
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..a932aacceb458b4863c6b1ce02fba015c7b3f03d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/main.py
@@ -0,0 +1,323 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(max_iter): # Renamed iterations to max_iter
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter, fine_tune_iter, radius_sim_iter,
+ learning_rate, wall_strength, initial_growth_pressure,
+ final_growth_pressure, fine_tune_lr):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ Parameters are now passed explicitly for better configurability.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter) # Use max_iter
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter) # Use max_iter
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population including grid-based and random individuals,
+ with an emphasis on structured initial placements for the 26th circle.
+ """
+ population = []
+
+ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
+ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate positions for the 26th circle, covering corners, edges, and interstitial spaces.
+ extra_pos_candidates = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.05, 0.5], [0.5, 0.05], [0.95, 0.5], [0.5, 0.95], # Edge midpoints
+ [0.25, 0.25], [0.75, 0.75], # Diagonal
+ [0.25, 0.75], [0.75, 0.25],
+ [0.3, 0.1], [0.7, 0.1], # more points slightly off grid
+ [0.1, 0.3], [0.1, 0.7]
+ ]
+
+ # Generate initial individuals from these grid configurations with slight perturbations
+ num_grid_starts_to_generate = min(population_size // 2, len(extra_pos_candidates) * 2) # Up to half population from structured starts
+ perturbation_strength_initial_grid = 0.015
+
+ for i in range(num_grid_starts_to_generate):
+ current_centers = np.zeros((n_circles, 2))
+ current_centers[:n_circles-1] = base_grid_centers_25.copy()
+
+ # Select an extra position candidate, cycling through them
+ extra_pos_idx = i % len(extra_pos_candidates)
+ extra_pos = extra_pos_candidates[extra_pos_idx]
+ current_centers[n_circles-1] = np.array(extra_pos)
+
+ # Apply a small perturbation to all circles in this configuration
+ current_centers += np.random.normal(0, perturbation_strength_initial_grid, (n_circles, 2))
+
+ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
+
+ # Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies Gaussian noise mutation to individual circle centers.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+
+ # Apply Gaussian noise only to selected circles
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ This version incorporates enhanced initial population diversity and
+ differentiated local search parameters for better performance.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 120 # Increased population for broader search
+ NUM_GENERATIONS = 800 # Increased generations for longer evolution
+ MUTATION_RATE = 0.18 # Balanced mutation rate
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial std dev for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final std dev (annealed)
+ TOURNAMENT_SIZE = 6 # Tournament size for stronger selection pressure
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Prob. to apply local search to a new child (reduced slightly for more GA exploration)
+
+ GA_RADIUS_ITER = 100 # Faster radius evaluation for GA loop
+ FINAL_RADIUS_ITER = 600 # High-precision for final best solution
+
+ # Local Search parameters for within the GA loop (lighter, faster iterations)
+ ga_local_search_params = {
+ 'sim_iter': 200, 'fine_tune_iter': 70, 'radius_sim_iter': 80,
+ 'learning_rate': 0.02, 'wall_strength': 0.8,
+ 'initial_growth_pressure': 1.04, 'final_growth_pressure': 1.001,
+ 'fine_tune_lr': 0.001
+ }
+
+ # Local Search parameters for the final polish (more intensive, run once)
+ final_local_search_params = {
+ 'sim_iter': 800, 'fine_tune_iter': 250, 'radius_sim_iter': 200,
+ 'learning_rate': 0.02, 'wall_strength': 0.85, # Slightly stronger wall repulsion
+ 'initial_growth_pressure': 1.05, 'final_growth_pressure': 1.001,
+ 'fine_tune_lr': 0.0005 # Even smaller learning rate for precision
+ }
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population with light local search
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy(), **ga_local_search_params)
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy(), **ga_local_search_params)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+ # Final intensive local search and evaluation of the best individual
+ best_individual_overall.centers = hybrid_local_searcher(best_individual_overall.centers.copy(), **final_local_search_params)
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..6868f07628cb6a6d9381e1a412c20756dd87bda8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/original.py
@@ -0,0 +1,256 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=200, fine_tune_iter=70, radius_sim_iter=70):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for robust local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 100 # Increased population for broader search
+ NUM_GENERATIONS = 600 # Increased generations due to faster evaluations
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7 # Increased tournament size for stronger selection pressure
+ ELITISM_COUNT = 6 # Increased elite count to preserve more good solutions
+ LOCAL_SEARCH_PROB = 0.25 # Reduced probability for local search to balance exploration/exploitation
+
+ GA_RADIUS_ITER = 70 # Faster radius evaluation for GA loop
+ FINAL_RADIUS_ITER = 500 # High-precision for final best solution
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+ # Final evaluation of the best individual with high precision
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..cb424af08edf835975352f4e31828aa7f12be108
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_116/search_replace.txt
@@ -0,0 +1,577 @@
+The current program uses a Memetic Algorithm (MA) which combines a Genetic Algorithm (GA) for global exploration with a force-directed local search for local exploitation. While the current implementation has a good foundation, its performance (2.29) indicates room for improvement compared to the best-known result (2.635).
+
+My strategy for improvement focuses on enhancing the diversity of initial solutions and refining how the local search is applied at different stages of the MA, drawing insights from successful previous programs (like G97) and general MA best practices.
+
+Here are the proposed changes:
+
+1. **Refactor `hybrid_local_searcher` to accept all parameters:** Currently, the `hybrid_local_searcher` function has hardcoded parameters. This limits its flexibility. By making all its tuning parameters explicit arguments, we can use different parameter sets (e.g., a lighter, faster set for within the GA loop and a more thorough, intensive set for the final solution polish). This was a strength of G97's class-based approach.
+
+2. **Differentiate Local Search Parameters and MA Hyperparameters:**
+ * **Lighter Local Search during GA:** For the local search applied to the initial population and probabilistically to children during the GA generations, we'll use a slightly less intensive set of parameters. This allows quicker evaluations within the GA loop, conserving computational resources while still providing effective local refinement. I will adjust `initial_growth_pressure` to be slightly less aggressive (1.04 instead of 1.05).
+ * **Intensive Final Local Search:** A more thorough and aggressive local search will be applied *once* to the best individual found by the GA. This is a critical step to achieve the highest possible sum of radii for the final solution. This will involve more `sim_iter`, `fine_tune_iter`, higher `radius_sim_iter`, and potentially stronger `wall_strength` and smaller `fine_tune_lr` for precise settling.
+ * **Adjust MA Hyperparameters:** Based on previous high-performing programs, I will adjust `POPULATION_SIZE`, `NUM_GENERATIONS`, `MUTATION_RATE`, `INITIAL_MUTATION_STRENGTH`, `TOURNAMENT_SIZE`, `ELITISM_COUNT`, and `LOCAL_SEARCH_PROB` to favor a slightly larger population, longer evolution, and a balanced exploration/exploitation ratio. Specifically, reducing `LOCAL_SEARCH_PROB` slightly (from 0.25 to 0.15) might encourage more GA-driven exploration before local search kicks in.
+
+3. **Enhance `initialize_population` for greater diversity:** The current initialization primarily uses a single 5x5 grid pattern (for 25 circles) with one interstitial point for the 26th, plus perturbed versions and random configs. This can be significantly improved by re-incorporating the diverse "extra position candidates" strategy from G97. This involves strategically placing the 26th circle in various common interstitial, corner-offset, or edge-midpoint positions, and then perturbing these configurations. This ensures the GA starts with a broader and geometrically more promising set of initial solutions.
+
+These changes aim to create a more robust and efficient Memetic Algorithm by optimizing the interplay between global exploration (GA) and local exploitation (force-directed simulation), as well as providing richer initial conditions.
+
+
+refactor_local_search_params
+
+
+
+This edit refactors the `hybrid_local_searcher` function to accept all its tuning parameters as arguments. This change is necessary to allow for different sets of local search parameters to be used at various stages of the Memetic Algorithm (e.g., a lighter search during GA generations and a more intensive search for the final solution). It removes the hardcoded parameter values from within the function body and replaces them with the passed arguments.
+
+Additionally, for clarity and consistency, the parameter `iterations` in `compute_max_radii` is renamed to `max_iter` to match common usage in previous successful programs and within the local searcher's calls.
+
+
+
+<<<<<<< SEARCH
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=200, fine_tune_iter=70, radius_sim_iter=70):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for robust local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+=======
+def compute_max_radii(centers, max_iter=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(max_iter): # Renamed iterations to max_iter
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter, fine_tune_iter, radius_sim_iter,
+ learning_rate, wall_strength, initial_growth_pressure,
+ final_growth_pressure, fine_tune_lr):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ Parameters are now passed explicitly for better configurability.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter) # Use max_iter
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter) # Use max_iter
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+>>>>>>> REPLACE
+
+
+
+optimized_ma_params_and_initialization
+
+
+
+This edit applies several key optimizations:
+1. **Tuned MA Hyperparameters:** Hyperparameters for `POPULATION_SIZE`, `NUM_GENERATIONS`, `MUTATION_RATE`, `INITIAL_MUTATION_STRENGTH`, `TOURNAMENT_SIZE`, `ELITISM_COUNT`, and `LOCAL_SEARCH_PROB` are adjusted to values known to work well in similar MA contexts (often inspired by G97 and other high-performing previous programs). These changes aim for a larger population, longer evolution, and a more balanced exploration/exploitation ratio.
+2. **Differentiated Local Search Parameters:** Two sets of local search parameters are defined: `ga_local_search_params` for the lighter, faster local searches applied within the GA loop, and `final_local_search_params` for a single, more intensive local search at the end to polish the best solution. This improves efficiency during evolution and maximizes precision at the final stage.
+3. **Enhanced Population Initialization:** The `initialize_population` function is significantly enhanced. Instead of relying on a single grid pattern, it now generates diverse initial configurations by combining a base 5x5 grid (for 25 circles) with a variety of strategically chosen `extra_pos_candidates` for the 26th circle (e.g., interstitial, corner-offset, edge-midpoint positions). Each of these configurations is then slightly perturbed, and purely random configurations fill the rest of the population. This provides a much broader and geometrically more promising set of starting points for the MA.
+4. **Corrected Fitness Evaluation Calls:** Calls to `compute_max_radii` now consistently use `max_iter` as the argument name.
+
+
+
+<<<<<<< SEARCH
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 100 # Increased population for broader search
+ NUM_GENERATIONS = 600 # Increased generations due to faster evaluations
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7 # Increased tournament size for stronger selection pressure
+ ELITISM_COUNT = 6 # Increased elite count to preserve more good solutions
+ LOCAL_SEARCH_PROB = 0.25 # Reduced probability for local search to balance exploration/exploitation
+
+ GA_RADIUS_ITER = 70 # Faster radius evaluation for GA loop
+ FINAL_RADIUS_ITER = 500 # High-precision for final best solution
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+ # Final evaluation of the best individual with high precision
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+=======
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population including grid-based and random individuals,
+ with an emphasis on structured initial placements for the 26th circle.
+ """
+ population = []
+
+ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
+ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate positions for the 26th circle, covering corners, edges, and interstitial spaces.
+ extra_pos_candidates = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.05, 0.5], [0.5, 0.05], [0.95, 0.5], [0.5, 0.95], # Edge midpoints
+ [0.25, 0.25], [0.75, 0.75], # Diagonal
+ [0.25, 0.75], [0.75, 0.25],
+ [0.3, 0.1], [0.7, 0.1], # more points slightly off grid
+ [0.1, 0.3], [0.1, 0.7]
+ ]
+
+ # Generate initial individuals from these grid configurations with slight perturbations
+ num_grid_starts_to_generate = min(population_size // 2, len(extra_pos_candidates) * 2) # Up to half population from structured starts
+ perturbation_strength_initial_grid = 0.015
+
+ for i in range(num_grid_starts_to_generate):
+ current_centers = np.zeros((n_circles, 2))
+ current_centers[:n_circles-1] = base_grid_centers_25.copy()
+
+ # Select an extra position candidate, cycling through them
+ extra_pos_idx = i % len(extra_pos_candidates)
+ extra_pos = extra_pos_candidates[extra_pos_idx]
+ current_centers[n_circles-1] = np.array(extra_pos)
+
+ # Apply a small perturbation to all circles in this configuration
+ current_centers += np.random.normal(0, perturbation_strength_initial_grid, (n_circles, 2))
+
+ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
+
+ # Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies Gaussian noise mutation to individual circle centers.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+
+ # Apply Gaussian noise only to selected circles
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ This version incorporates enhanced initial population diversity and
+ differentiated local search parameters for better performance.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 120 # Increased population for broader search
+ NUM_GENERATIONS = 800 # Increased generations for longer evolution
+ MUTATION_RATE = 0.18 # Balanced mutation rate
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial std dev for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final std dev (annealed)
+ TOURNAMENT_SIZE = 6 # Tournament size for stronger selection pressure
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Prob. to apply local search to a new child (reduced slightly for more GA exploration)
+
+ GA_RADIUS_ITER = 100 # Faster radius evaluation for GA loop
+ FINAL_RADIUS_ITER = 600 # High-precision for final best solution
+
+ # Local Search parameters for within the GA loop (lighter, faster iterations)
+ ga_local_search_params = {
+ 'sim_iter': 200, 'fine_tune_iter': 70, 'radius_sim_iter': 80,
+ 'learning_rate': 0.02, 'wall_strength': 0.8,
+ 'initial_growth_pressure': 1.04, 'final_growth_pressure': 1.001,
+ 'fine_tune_lr': 0.001
+ }
+
+ # Local Search parameters for the final polish (more intensive, run once)
+ final_local_search_params = {
+ 'sim_iter': 800, 'fine_tune_iter': 250, 'radius_sim_iter': 200,
+ 'learning_rate': 0.02, 'wall_strength': 0.85, # Slightly stronger wall repulsion
+ 'initial_growth_pressure': 1.05, 'final_growth_pressure': 1.001,
+ 'fine_tune_lr': 0.0005 # Even smaller learning rate for precision
+ }
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population with light local search
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy(), **ga_local_search_params)
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy(), **ga_local_search_params)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Use GA_RADIUS_ITER here
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a new Individual object to ensure the best solution's state is preserved
+ # and not modified by subsequent GA operations if it happens to be an elite.
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness # Update fitness as well
+ best_individual_overall.radii = current_best_in_gen.radii # Copy radii too if available
+
+ # Final intensive local search and evaluation of the best individual
+ best_individual_overall.centers = hybrid_local_searcher(best_individual_overall.centers.copy(), **final_local_search_params)
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..10dc905db24c59c4190ba5e1fd8f06c5e50e8eb8
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..42709e9d4baf1a0e87d0b56327704d9c6ed0f599
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/edit.diff
@@ -0,0 +1,332 @@
+--- a/original.py
++++ b/original.py
+@@ -1,234 +1,285 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+-class LocalSearchRefiner:
+- """A class to perform fast, local refinement on a set of centers."""
+- def __init__(self, iterations, learning_rate, wall_strength):
+- self.iterations = iterations
+- self.lr = learning_rate
+- self.wall_strength = wall_strength
++class HybridLocalSearcher:
++ """
++ A powerful local search refiner based on a two-phase physical simulation,
++ reinstated from high-performing prior versions. This approach uses "growth
++ pressure" to escape local minima.
++ """
++ def __init__(self, n, config):
++ self.n = n
++ self.config = config
++
++ def _compute_radii_for_sim(self, centers):
++ """A fast radius computation for use inside the simulation loop."""
++ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+- """Applies a few steps of force-directed refinement to polish a solution."""
++ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+- for _ in range(self.iterations):
+- # Use faster radius calculation for local search
+- radii = compute_max_radii(refined_centers, max_iter=100)
+-
+- # --- Vectorized Force Calculation (no growth pressure) ---
+- # Circle-to-circle forces
++
++ # Phase 1: Main refinement with decreasing growth pressure
++ iterations = self.config['sim_iter']
++ base_lr = self.config['learning_rate']
++ wall_strength = self.config['wall_strength']
++ initial_growth_pressure = self.config['initial_growth_pressure']
++ final_growth_pressure = self.config['final_growth_pressure']
++
++ for i_iter in range(iterations):
++ progress = i_iter / iterations
++ # Anneal growth pressure and learning rate quadratically for a smooth decrease
++ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++ current_radii = self._compute_radii_for_sim(refined_centers)
++ pressured_radii = current_radii * current_growth_pressure
++
++ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+-
+- radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+-
+- force_mags = overlaps
+- force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+- # Wall forces
++ # Wall forces calculation
+ wall_forces = np.zeros_like(refined_centers)
+- wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+- wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
++ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+-
+- refined_centers += forces * self.lr
++ lr = base_lr * (1.0 - progress)**2
++ refined_centers += forces * lr
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ # Phase 2: Fine-tuning with minimal growth pressure
++ iterations_ft = self.config['fine_tune_iter']
++ lr_ft = self.config['fine_tune_lr']
++ for _ in range(iterations_ft):
++ current_radii = self._compute_radii_for_sim(refined_centers)
++
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ wall_forces = np.zeros_like(refined_centers)
++ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+ class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+- self.local_search_refiner = LocalSearchRefiner(
+- iterations=config['ls_iter'],
+- learning_rate=config['ls_lr'],
+- wall_strength=config['ls_wall_strength']
+- )
++ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+- num_grid_based = self.config['population_size'] // 4
++ num_grid_based = self.config['population_size'] // 2
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
++ # Use a more diverse set of candidate positions for the 26th circle, inspired by high-performing priors.
+ interstitial_points = [
+- [spacing, spacing], [spacing, 1 - spacing],
+- [1 - spacing, spacing], [1 - spacing, 1 - spacing]
++ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
++ [0.5, 0.5], # Center
++ [spacing, spacing], [spacing, 1-spacing], [1-spacing, spacing], [1-spacing, 1-spacing], # Grid interstitial
++ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+- if np.random.rand() < 0.02: # 2% chance of a jump mutation
++ if np.random.rand() < self.config['jump_mutation_prob']: # Chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+- 'population_size': 60,
+- 'generations': 300,
+- 'elite_count': 4,
+- 'tournament_size': 5,
+- 'mutation_rate': 0.3,
++ # MA parameters - tuned for more exploration and stronger selection
++ 'population_size': 80,
++ 'generations': 350,
++ 'elite_count': 5,
++ 'tournament_size': 6,
++ 'mutation_rate': 0.4, # Per-circle mutation probability
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+- # Memetic Parameters
+- 'local_search_prob': 0.15, # Probability of applying local search to a new child
+- 'ls_iter': 30, # Iterations for the local search refinement
+- 'ls_lr': 0.003, # Learning rate for the local search
+- 'ls_wall_strength': 0.2,
++ 'jump_mutation_prob': 0.03, # Chance to completely reset one circle's position
++
++ # Memetic Parameters - increased probability to leverage powerful LS
++ 'local_search_prob': 0.20,
++ 'ls_config': {
++ # Phase 1: Aggressive refinement
++ 'sim_iter': 450,
++ 'learning_rate': 0.02,
++ 'initial_growth_pressure': 1.06,
++ 'final_growth_pressure': 1.001,
++ # Phase 2: Fine-tuning
++ 'fine_tune_iter': 250,
++ 'fine_tune_lr': 0.0015,
++ # Shared parameters
++ 'wall_strength': 0.65,
++ 'radius_sim_iter': 150, # Iterations for radius calculation within LS
++ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..d85dccb23bd702b99b0b4e9a58d3d2585d19c19a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/main.py
@@ -0,0 +1,285 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation,
+ reinstated from high-performing prior versions. This approach uses "growth
+ pressure" to escape local minima.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically for a smooth decrease
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces calculation
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 2
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ # Use a more diverse set of candidate positions for the 26th circle, inspired by high-performing priors.
+ interstitial_points = [
+ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
+ [0.5, 0.5], # Center
+ [spacing, spacing], [spacing, 1-spacing], [1-spacing, spacing], [1-spacing, 1-spacing], # Grid interstitial
+ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']: # Chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # MA parameters - tuned for more exploration and stronger selection
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 5,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.4, # Per-circle mutation probability
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'jump_mutation_prob': 0.03, # Chance to completely reset one circle's position
+
+ # Memetic Parameters - increased probability to leverage powerful LS
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+ # Phase 1: Aggressive refinement
+ 'sim_iter': 450,
+ 'learning_rate': 0.02,
+ 'initial_growth_pressure': 1.06,
+ 'final_growth_pressure': 1.001,
+ # Phase 2: Fine-tuning
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ # Shared parameters
+ 'wall_strength': 0.65,
+ 'radius_sim_iter': 150, # Iterations for radius calculation within LS
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd842822cfbe7b9346930cde45e0df1e9fdce374
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/original.py
@@ -0,0 +1,234 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers."""
+ def __init__(self, iterations, learning_rate, wall_strength):
+ self.iterations = iterations
+ self.lr = learning_rate
+ self.wall_strength = wall_strength
+
+ def refine(self, centers):
+ """Applies a few steps of force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ for _ in range(self.iterations):
+ # Use faster radius calculation for local search
+ radii = compute_max_radii(refined_centers, max_iter=100)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # Circle-to-circle forces
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(
+ iterations=config['ls_iter'],
+ learning_rate=config['ls_lr'],
+ wall_strength=config['ls_wall_strength']
+ )
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 60,
+ 'generations': 300,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.3,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+ 'ls_iter': 30, # Iterations for the local search refinement
+ 'ls_lr': 0.003, # Learning rate for the local search
+ 'ls_wall_strength': 0.2,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..716b2e506d485c977f24b0a688d62481b28f5d1a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results
+Run 1/1 completed in 7848.63 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.2926690607640166
+ public: {'centers_str': ' centers[0] = (0.0814, 0.1117)\n centers[1] = (0.1908, 0.0437)\n centers[2] = (0.5919, 0.1365)\n centers[3] = (0.7604, 0.0518)\n centers[4] = (0.9078, 0.0938)\n centers[5] = (0.0553, 0.2485)\n centers[6] = (0.6024, 0.6129)\n centers[7] = (0.5233, 0.3139)\n centers[8] = (0.7263, 0.2327)\n centers[9] = (0.9274, 0.2669)\n centers[10] = (0.0607, 0.5228)\n centers[11] = (0.1667, 0.3734)\n centers[12] = (0.3965, 0.4378)\n centers[13] = (0.6260, 0.4580)\n centers[14] = (0.8694, 0.4660)\n centers[15] = (0.0298, 0.6896)\n centers[16] = (0.2269, 0.6650)\n centers[17] = (0.4914, 0.6270)\n centers[18] = (0.7222, 0.6975)\n centers[19] = (0.9306, 0.6688)\n centers[20] = (0.0912, 0.8872)\n centers[21] = (0.2616, 0.9181)\n centers[22] = (0.4733, 0.8632)\n centers[23] = (0.6899, 0.9193)\n centers[24] = (0.8899, 0.8867)\n centers[25] = (0.3591, 0.1611)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.2926690607640166}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/packing_viz.png
+ execution_time_mean: 7848.630926954094
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..17fc5ae8c2e3f6a1017eaf4088a91c3599e76ea9
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.2926690607640166,
+ "public": {
+ "centers_str": " centers[0] = (0.0814, 0.1117)\n centers[1] = (0.1908, 0.0437)\n centers[2] = (0.5919, 0.1365)\n centers[3] = (0.7604, 0.0518)\n centers[4] = (0.9078, 0.0938)\n centers[5] = (0.0553, 0.2485)\n centers[6] = (0.6024, 0.6129)\n centers[7] = (0.5233, 0.3139)\n centers[8] = (0.7263, 0.2327)\n centers[9] = (0.9274, 0.2669)\n centers[10] = (0.0607, 0.5228)\n centers[11] = (0.1667, 0.3734)\n centers[12] = (0.3965, 0.4378)\n centers[13] = (0.6260, 0.4580)\n centers[14] = (0.8694, 0.4660)\n centers[15] = (0.0298, 0.6896)\n centers[16] = (0.2269, 0.6650)\n centers[17] = (0.4914, 0.6270)\n centers[18] = (0.7222, 0.6975)\n centers[19] = (0.9306, 0.6688)\n centers[20] = (0.0912, 0.8872)\n centers[21] = (0.2616, 0.9181)\n centers[22] = (0.4733, 0.8632)\n centers[23] = (0.6899, 0.9193)\n centers[24] = (0.8899, 0.8867)\n centers[25] = (0.3591, 0.1611)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.2926690607640166
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/results/packing_viz.png",
+ "execution_time_mean": 7848.630926954094,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9224ea7522861fbad5a489e0a520ce47a3905358
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_117/search_replace.txt
@@ -0,0 +1,285 @@
+I understand the previous submission failed due to a search-and-replace error. I will correct this by ensuring the `SEARCH` blocks are copied verbatim, including indentation, and by breaking the changes into smaller, more targeted edits.
+
+My strategy is to revert the `LocalSearchRefiner` to the more powerful, two-phase `HybridLocalSearcher` from a previously successful implementation. This is a crucial component of an effective Memetic Algorithm. To support this, I will also adjust the algorithm's configuration to enhance population diversity and tune the new local searcher's parameters for optimal performance.
+
+
+reinstate_hybrid_local_searcher
+
+
+The current `LocalSearchRefiner` is a very basic force-directed algorithm that lacks the power to escape local minima effectively. Its performance is significantly lower than that of previous successful versions.
+
+This change reinstates the much more powerful `HybridLocalSearcher` from a high-scoring prior submission. This searcher implements a two-phase physical simulation:
+1. **An aggressive refinement phase** with high, annealing "growth pressure" and learning rates. This phase "unfreezes" the configuration by temporarily inflating circle radii, forcing them apart to explore new, potentially better arrangements.
+2. **A fine-tuning phase** with low pressure and a small learning rate to gently settle the circles into a stable, high-quality local optimum after the aggressive exploration.
+
+This sophisticated local search is the core of a successful Memetic Algorithm and is expected to provide a substantial performance boost by enabling more effective exploitation of the search space.
+
+
+<<<<<<< SEARCH
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers."""
+ def __init__(self, iterations, learning_rate, wall_strength):
+ self.iterations = iterations
+ self.lr = learning_rate
+ self.wall_strength = wall_strength
+
+ def refine(self, centers):
+ """Applies a few steps of force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ for _ in range(self.iterations):
+ # Use faster radius calculation for local search
+ radii = compute_max_radii(refined_centers, max_iter=100)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # Circle-to-circle forces
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+=======
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation,
+ reinstated from high-performing prior versions. This approach uses "growth
+ pressure" to escape local minima.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically for a smooth decrease
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces calculation
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+>>>>>>> REPLACE
+
+
+configure_hybrid_ma
+
+
+This series of edits integrates the new `HybridLocalSearcher` and tunes the overall algorithm for better performance.
+
+1. **MA `__init__`**: The `MemeticAlgorithm`'s initializer is updated to instantiate `HybridLocalSearcher` and pass it a dedicated `ls_config` dictionary from the main configuration.
+2. **Population Initialization**: The `_initialize_population` method is enhanced. It now dedicates half the population to strategic starts (up from one-quarter) and uses a much more diverse list of candidate positions for the 26th circle, including corners, the center, and edge midpoints, to seed the search in more promising regions.
+3. **Mutation Operator**: The hardcoded "jump mutation" probability in the `_mutate` method is replaced with a configurable parameter, `jump_mutation_prob`, for easier tuning.
+4. **Hyperparameter Tuning**: The main `config` dictionary is overhauled. The population size, generations, and tournament size are increased for more thorough exploration and stronger selection. A new `ls_config` dictionary is added to precisely control the parameters of the `HybridLocalSearcher`, including aggressive growth pressure, learning rates, and simulation iterations.
+
+
+<<<<<<< SEARCH
+ self.local_search_refiner = LocalSearchRefiner(
+ iterations=config['ls_iter'],
+ learning_rate=config['ls_lr'],
+ wall_strength=config['ls_wall_strength']
+ )
+=======
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+=======
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 2
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ # Use a more diverse set of candidate positions for the 26th circle, inspired by high-performing priors.
+ interstitial_points = [
+ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
+ [0.5, 0.5], # Center
+ [spacing, spacing], [spacing, 1-spacing], [1-spacing, spacing], [1-spacing, 1-spacing], # Grid interstitial
+ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+=======
+ if np.random.rand() < self.config['jump_mutation_prob']: # Chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ config = {
+ 'population_size': 60,
+ 'generations': 300,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.3,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+ 'ls_iter': 30, # Iterations for the local search refinement
+ 'ls_lr': 0.003, # Learning rate for the local search
+ 'ls_wall_strength': 0.2,
+ }
+=======
+ config = {
+ # MA parameters - tuned for more exploration and stronger selection
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 5,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.4, # Per-circle mutation probability
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'jump_mutation_prob': 0.03, # Chance to completely reset one circle's position
+
+ # Memetic Parameters - increased probability to leverage powerful LS
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+ # Phase 1: Aggressive refinement
+ 'sim_iter': 450,
+ 'learning_rate': 0.02,
+ 'initial_growth_pressure': 1.06,
+ 'final_growth_pressure': 1.001,
+ # Phase 2: Fine-tuning
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ # Shared parameters
+ 'wall_strength': 0.65,
+ 'radius_sim_iter': 150, # Iterations for radius calculation within LS
+ }
+ }
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..aa6537b03e985a7e35447cf1ff8ff8b18c08f43f
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..0e0d5b83f5ed246605535b1c1af96a59f956eb77
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/edit.diff
@@ -0,0 +1,263 @@
+--- a/original.py
++++ b/original.py
+@@ -1,216 +1,247 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+ import math
+ import random
+
+ def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+- self.move_weights = config['move_weights']
++ self.move_weights_start = config['move_weights_start']
++ self.move_weights_end = config['move_weights_end']
++ self.cluster_size_start = config['cluster_size_start']
++ self.cluster_size_end = config['cluster_size_end']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+- # Choose a move type based on weights
+- move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+-
+- # Anneal step size
+- t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+- current_step_size = self.max_step_size * t_progress
+-
+- return move_func(current_step_size)
++ # Calculate t_progress_ratio: 1.0 at t_start, 0.0 at t_end
++ if self.config['t_start'] == self.config['t_end']:
++ t_progress_ratio = 0.0
++ else:
++ t_progress_ratio = max(0.0, (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end']))
++
++ # Anneal move weights: from start_weights at high T to end_weights at low T
++ current_move_weights = [
++ s * t_progress_ratio + e * (1 - t_progress_ratio)
++ for s, e in zip(self.move_weights_start, self.move_weights_end)
++ ]
++ # Normalize weights to sum to 1
++ sum_weights = sum(current_move_weights)
++ if sum_weights > 0:
++ current_move_weights = [w / sum_weights for w in current_move_weights]
++ else: # Fallback if all weights are zero (shouldn't happen with reasonable config)
++ current_move_weights = [1.0 / len(self.move_types)] * len(self.move_types)
++
++ move_func = random.choices(self.move_types, weights=current_move_weights, k=1)[0]
++
++ # Anneal step size: current code uses linear decay, which is fine
++ current_step_size = self.max_step_size * t_progress_ratio
++
++ # Anneal cluster size: from cluster_size_start at high T to cluster_size_end at low T
++ current_cluster_size = round(self.cluster_size_start * t_progress_ratio + self.cluster_size_end * (1 - t_progress_ratio))
++ current_cluster_size = max(1, min(self.n, int(current_cluster_size))) # Ensure integer and valid range
++
++ # Pass current_cluster_size to _move_cluster if it's the chosen move
++ if move_func == self._move_cluster:
++ return move_func(current_step_size, current_cluster_size)
++ else:
++ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+- def _move_cluster(self, step_size):
++ def _move_cluster(self, step_size, cluster_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+- cluster_size = self.config['cluster_size']
++ # cluster_size is now passed as an argument
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 12, # Number of independent SA runs
+ 't_start': 0.05, # Initial temperature
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.998, # Geometric cooling factor
+ 'moves_per_temp': 60, # Number of moves at each temperature step
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
+ 'radius_iter': 250, # Radius calculation iterations during search
+- 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+- 'cluster_size': 4,
++ # Annealed move weights: [single_circle, cluster, swap_circles]
++ 'move_weights_start': [0.30, 0.45, 0.25], # More exploration (cluster & swap) at high T
++ 'move_weights_end': [0.80, 0.15, 0.05], # More exploitation (single) at low T
++ # Annealed cluster size
++ 'cluster_size_start': 8, # Larger clusters for broad moves at high T
++ 'cluster_size_end': 2, # Smaller clusters for fine-tuning at low T
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Place the 26th circle in a strategic corner or center
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..b725fff47fbe57db14f1852faf8623e8f68ea233
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/main.py
@@ -0,0 +1,247 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights_start = config['move_weights_start']
+ self.move_weights_end = config['move_weights_end']
+ self.cluster_size_start = config['cluster_size_start']
+ self.cluster_size_end = config['cluster_size_end']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Calculate t_progress_ratio: 1.0 at t_start, 0.0 at t_end
+ if self.config['t_start'] == self.config['t_end']:
+ t_progress_ratio = 0.0
+ else:
+ t_progress_ratio = max(0.0, (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end']))
+
+ # Anneal move weights: from start_weights at high T to end_weights at low T
+ current_move_weights = [
+ s * t_progress_ratio + e * (1 - t_progress_ratio)
+ for s, e in zip(self.move_weights_start, self.move_weights_end)
+ ]
+ # Normalize weights to sum to 1
+ sum_weights = sum(current_move_weights)
+ if sum_weights > 0:
+ current_move_weights = [w / sum_weights for w in current_move_weights]
+ else: # Fallback if all weights are zero (shouldn't happen with reasonable config)
+ current_move_weights = [1.0 / len(self.move_types)] * len(self.move_types)
+
+ move_func = random.choices(self.move_types, weights=current_move_weights, k=1)[0]
+
+ # Anneal step size: current code uses linear decay, which is fine
+ current_step_size = self.max_step_size * t_progress_ratio
+
+ # Anneal cluster size: from cluster_size_start at high T to cluster_size_end at low T
+ current_cluster_size = round(self.cluster_size_start * t_progress_ratio + self.cluster_size_end * (1 - t_progress_ratio))
+ current_cluster_size = max(1, min(self.n, int(current_cluster_size))) # Ensure integer and valid range
+
+ # Pass current_cluster_size to _move_cluster if it's the chosen move
+ if move_func == self._move_cluster:
+ return move_func(current_step_size, current_cluster_size)
+ else:
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size, cluster_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ # cluster_size is now passed as an argument
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 12, # Number of independent SA runs
+ 't_start': 0.05, # Initial temperature
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.998, # Geometric cooling factor
+ 'moves_per_temp': 60, # Number of moves at each temperature step
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
+ 'radius_iter': 250, # Radius calculation iterations during search
+ # Annealed move weights: [single_circle, cluster, swap_circles]
+ 'move_weights_start': [0.30, 0.45, 0.25], # More exploration (cluster & swap) at high T
+ 'move_weights_end': [0.80, 0.15, 0.05], # More exploitation (single) at low T
+ # Annealed cluster size
+ 'cluster_size_start': 8, # Larger clusters for broad moves at high T
+ 'cluster_size_end': 2, # Smaller clusters for fine-tuning at low T
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Place the 26th circle in a strategic corner or center
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ff95302fa28431aa45cfdecfc5bf767e17899a4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/original.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 12, # Number of independent SA runs
+ 't_start': 0.05, # Initial temperature
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.998, # Geometric cooling factor
+ 'moves_per_temp': 60, # Number of moves at each temperature step
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+ 'cluster_size': 4,
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Place the 26th circle in a strategic corner or center
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..029713e944838e56267b65a699237fff98cea05e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_118/search_replace.txt
@@ -0,0 +1,126 @@
+
+annealed_sa_params
+
+
+
+This edit implements an annealing schedule for the Simulated Annealer's move weights and cluster size, as suggested by recommendation 1.
+
+**Rationale:**
+1. **Annealed Move Weights:** At high temperatures (beginning of the search), the algorithm should favor more global exploration. `_move_cluster` and `_swap_circles` are better for this as they change multiple circle positions or swap distinct positions. As the temperature cools, the search should become more exploitative, focusing on fine-tuning. `_move_single_circle` is ideal for this. The `move_weights_start` configures higher probabilities for cluster and swap moves, transitioning to `move_weights_end` which heavily favors single circle moves.
+2. **Annealed Cluster Size:** Similar to move weights, a larger `cluster_size` at the start allows for bigger, more exploratory shifts in groups of circles. As temperature decreases, a smaller `cluster_size` facilitates more localized adjustments. The `cluster_size_start` and `cluster_size_end` parameters handle this transition.
+
+These changes allow the Simulated Annealer to adapt its search strategy throughout the annealing process, promoting broader exploration early on and finer exploitation later, which generally leads to better solutions in optimization problems like circle packing.
+
+
+
+<<<<<<< SEARCH
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+=======
+ self.move_weights_start = config['move_weights_start']
+ self.move_weights_end = config['move_weights_end']
+ self.cluster_size_start = config['cluster_size_start']
+ self.cluster_size_end = config['cluster_size_end']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+>>>>>>> REPLACE
+
+
+
+<<<<<<< SEARCH
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+=======
+ # Calculate t_progress_ratio: 1.0 at t_start, 0.0 at t_end
+ if self.config['t_start'] == self.config['t_end']:
+ t_progress_ratio = 0.0
+ else:
+ t_progress_ratio = max(0.0, (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end']))
+
+ # Anneal move weights: from start_weights at high T to end_weights at low T
+ current_move_weights = [
+ s * t_progress_ratio + e * (1 - t_progress_ratio)
+ for s, e in zip(self.move_weights_start, self.move_weights_end)
+ ]
+ # Normalize weights to sum to 1
+ sum_weights = sum(current_move_weights)
+ if sum_weights > 0:
+ current_move_weights = [w / sum_weights for w in current_move_weights]
+ else: # Fallback if all weights are zero (shouldn't happen with reasonable config)
+ current_move_weights = [1.0 / len(self.move_types)] * len(self.move_types)
+
+ move_func = random.choices(self.move_types, weights=current_move_weights, k=1)[0]
+
+ # Anneal step size: current code uses linear decay, which is fine
+ current_step_size = self.max_step_size * t_progress_ratio
+
+ # Anneal cluster size: from cluster_size_start at high T to cluster_size_end at low T
+ current_cluster_size = round(self.cluster_size_start * t_progress_ratio + self.cluster_size_end * (1 - t_progress_ratio))
+ current_cluster_size = max(1, min(self.n, int(current_cluster_size))) # Ensure integer and valid range
+
+ # Pass current_cluster_size to _move_cluster if it's the chosen move
+ if move_func == self._move_cluster:
+ return move_func(current_step_size, current_cluster_size)
+ else:
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size, cluster_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ # cluster_size is now passed as an argument
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+>>>>>>> REPLACE
+
+
+
+<<<<<<< SEARCH
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+ 'cluster_size': 4,
+ }
+=======
+ 'radius_iter': 250, # Radius calculation iterations during search
+ # Annealed move weights: [single_circle, cluster, swap_circles]
+ 'move_weights_start': [0.30, 0.45, 0.25], # More exploration (cluster & swap) at high T
+ 'move_weights_end': [0.80, 0.15, 0.05], # More exploitation (single) at low T
+ # Annealed cluster size
+ 'cluster_size_start': 8, # Larger clusters for broad moves at high T
+ 'cluster_size_end': 2, # Smaller clusters for fine-tuning at low T
+ }
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..edc9cb25b369f6f4ac028135f2af1596525b222f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/edit.diff
@@ -0,0 +1,315 @@
+--- a/original.py
++++ b/original.py
+@@ -1,258 +1,286 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ # --- Core Utility: Radius Calculation ---
+ # Adopted from constructor; more robust with convergence check.
+ def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+ # --- NEW STRUCTURE: Encapsulated Local Searcher ---
+ class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer. This class encapsulates the
+ two-phase physics simulation logic, making it a reusable component.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.03,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning ---
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+ def _calculate_forces(self, centers, radii):
+ """Calculates repulsion forces from circle overlaps and wall proximity."""
+ forces = np.zeros_like(centers)
+
+ # Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Wall repulsion
+ forces[:, 0] += self.wall_strength * np.maximum(0, radii - centers[:, 0])
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + radii) - 1.0)
+ forces[:, 1] += self.wall_strength * np.maximum(0, radii - centers[:, 1])
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + radii) - 1.0)
+
+ return forces
+
+ # --- GA Core Components ---
+ class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+ def initialize_population(n_circles, population_size):
+ """Initializes a diverse population, including known good starting points."""
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + interstitial
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Perturbed versions of the baseline
+ for _ in range(population_size // 10):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.01, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Random configurations for diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+ def select_parents(population, tournament_size):
+ """Selects two parents using tournament selection."""
+ tournament = np.random.choice(population, tournament_size, replace=False)
+ tournament = sorted(tournament, key=lambda ind: ind.fitness, reverse=True)
+ return tournament[0], tournament[1]
+
+ def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover."""
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles) < 0.5
+ child_centers[mask] = parent1.centers[mask]
+ child_centers[~mask] = parent2.centers[~mask]
+ return Individual(child_centers, n_circles)
+
+ def mutate(individual, mutation_rate, mutation_strength, n_circles):
+- """Applies simple Gaussian noise mutation."""
++ """
++ Applies an enhanced mutation operator with multiple strategies for better exploration:
++ 1. Standard Gaussian Jitter (local fine-tuning).
++ 2. Swap Mutation (topological change).
++ 3. Re-initialize Mutation (escape deep local optima).
++ """
+ mutated_centers = individual.centers.copy()
+- for i in range(n_circles):
+- if np.random.rand() < mutation_rate:
+- mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+- individual.centers = np.clip(mutated_centers, 0.0, 1.0)
++
++ # --- Strategy 1: Standard Gaussian Jitter ---
++ mask = np.random.rand(n_circles) < mutation_rate
++ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
++ mutated_centers[mask] += noise[mask]
++
++ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
++ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
++ if n_circles > 1:
++ i, j = np.random.choice(n_circles, 2, replace=False)
++ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
++
++ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
++ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
++ # Pick one random circle and move it to a new random location.
++ idx_to_reinit = np.random.randint(0, n_circles)
++ mutated_centers[idx_to_reinit] = np.random.rand(2)
++
++ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+ # --- Main Orchestrator: Memetic Algorithm ---
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters ---
+- POPULATION_SIZE = 80
+- NUM_GENERATIONS = 500
+- MUTATION_RATE = 0.20
+- INITIAL_MUTATION_STRENGTH = 0.05
+- FINAL_MUTATION_STRENGTH = 0.001
+- TOURNAMENT_SIZE = 5
+- ELITISM_COUNT = 5
+- LOCAL_SEARCH_PROB = 0.1 # Probability to apply the powerful local search
++ POPULATION_SIZE = 120 # Increased population for broader search
++ NUM_GENERATIONS = 800 # More generations for better convergence
++ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
++ INITIAL_MUTATION_STRENGTH = 0.08 # Larger initial mutations for exploration
++ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
++ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
++ ELITISM_COUNT = 8 # Preserve more of the best solutions
++ LOCAL_SEARCH_PROB = 0.20 # Increased probability of applying local search
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+- local_searcher = HybridLocalSearcher() # Using default (tuned) params
+-
++ local_searcher = HybridLocalSearcher()
++
++ # --- Crucial Memetic Step: Initial Local Optimization ---
++ # Apply local search to the entire initial population to create a strong starting gene pool
++ # of locally-optimal individuals. This is a key feature of successful memetic algorithms.
+ for individual in population:
++ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+- # --- Main GA Loop ---
++ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+- (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
++ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate new individuals
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+- # --- Hybrid Operator: Probabilistic Local Search and Simple Mutation ---
+- # 1. Probabilistically apply the powerful but expensive local search
++ # --- Operator Chain: Mutate then (probabilistically) Local Search ---
++ # 1. Apply mutation to introduce variation
++ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
++
++ # 2. Probabilistically apply local search to polish the new child
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+- child.centers = local_searcher.optimize(child.centers)
+-
+- # 2. Always apply a small standard mutation for diversity
+- child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
++ child.centers = local_searcher.optimize(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+- best_individual_overall = current_best_in_gen
+-
+- # Final high-precision evaluation
++ # Create a true copy to prevent the best solution from being modified by reference
++ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
++ best_individual_overall.fitness = current_best_in_gen.fitness
++
++ # Final polish on the best solution found with a more intensive local search
++ final_searcher = HybridLocalSearcher(sim_iter=600, fine_tune_iter=250, radius_sim_iter=200)
++ best_individual_overall.centers = final_searcher.optimize(best_individual_overall.centers.copy())
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..249bc13095218317c27ef82a4be4492e6fc2adac
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/main.py
@@ -0,0 +1,286 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# --- NEW STRUCTURE: Encapsulated Local Searcher ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer. This class encapsulates the
+ two-phase physics simulation logic, making it a reusable component.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.03,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning ---
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+ def _calculate_forces(self, centers, radii):
+ """Calculates repulsion forces from circle overlaps and wall proximity."""
+ forces = np.zeros_like(centers)
+
+ # Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Wall repulsion
+ forces[:, 0] += self.wall_strength * np.maximum(0, radii - centers[:, 0])
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + radii) - 1.0)
+ forces[:, 1] += self.wall_strength * np.maximum(0, radii - centers[:, 1])
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + radii) - 1.0)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population, including known good starting points."""
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + interstitial
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Perturbed versions of the baseline
+ for _ in range(population_size // 10):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.01, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Random configurations for diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two parents using tournament selection."""
+ tournament = np.random.choice(population, tournament_size, replace=False)
+ tournament = sorted(tournament, key=lambda ind: ind.fitness, reverse=True)
+ return tournament[0], tournament[1]
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover."""
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles) < 0.5
+ child_centers[mask] = parent1.centers[mask]
+ child_centers[~mask] = parent2.centers[~mask]
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies for better exploration:
+ 1. Standard Gaussian Jitter (local fine-tuning).
+ 2. Swap Mutation (topological change).
+ 3. Re-initialize Mutation (escape deep local optima).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ mutated_centers[mask] += noise[mask]
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters ---
+ POPULATION_SIZE = 120 # Increased population for broader search
+ NUM_GENERATIONS = 800 # More generations for better convergence
+ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.08 # Larger initial mutations for exploration
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Preserve more of the best solutions
+ LOCAL_SEARCH_PROB = 0.20 # Increased probability of applying local search
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher()
+
+ # --- Crucial Memetic Step: Initial Local Optimization ---
+ # Apply local search to the entire initial population to create a strong starting gene pool
+ # of locally-optimal individuals. This is a key feature of successful memetic algorithms.
+ for individual in population:
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate new individuals
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Operator Chain: Mutate then (probabilistically) Local Search ---
+ # 1. Apply mutation to introduce variation
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # 2. Probabilistically apply local search to polish the new child
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a true copy to prevent the best solution from being modified by reference
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+
+ # Final polish on the best solution found with a more intensive local search
+ final_searcher = HybridLocalSearcher(sim_iter=600, fine_tune_iter=250, radius_sim_iter=200)
+ best_individual_overall.centers = final_searcher.optimize(best_individual_overall.centers.copy())
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..dced84c86d45e934c99da8444b7b718a415d430f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/original.py
@@ -0,0 +1,258 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# --- NEW STRUCTURE: Encapsulated Local Searcher ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer. This class encapsulates the
+ two-phase physics simulation logic, making it a reusable component.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.03,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning ---
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+ def _calculate_forces(self, centers, radii):
+ """Calculates repulsion forces from circle overlaps and wall proximity."""
+ forces = np.zeros_like(centers)
+
+ # Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Wall repulsion
+ forces[:, 0] += self.wall_strength * np.maximum(0, radii - centers[:, 0])
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + radii) - 1.0)
+ forces[:, 1] += self.wall_strength * np.maximum(0, radii - centers[:, 1])
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + radii) - 1.0)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population, including known good starting points."""
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + interstitial
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Perturbed versions of the baseline
+ for _ in range(population_size // 10):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.01, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Random configurations for diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two parents using tournament selection."""
+ tournament = np.random.choice(population, tournament_size, replace=False)
+ tournament = sorted(tournament, key=lambda ind: ind.fitness, reverse=True)
+ return tournament[0], tournament[1]
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover."""
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles) < 0.5
+ child_centers[mask] = parent1.centers[mask]
+ child_centers[~mask] = parent2.centers[~mask]
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies simple Gaussian noise mutation."""
+ mutated_centers = individual.centers.copy()
+ for i in range(n_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0)
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 500
+ MUTATION_RATE = 0.20
+ INITIAL_MUTATION_STRENGTH = 0.05
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 5
+ ELITISM_COUNT = 5
+ LOCAL_SEARCH_PROB = 0.1 # Probability to apply the powerful local search
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Using default (tuned) params
+
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main GA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate new individuals
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search and Simple Mutation ---
+ # 1. Probabilistically apply the powerful but expensive local search
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers)
+
+ # 2. Always apply a small standard mutation for diversity
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Final high-precision evaluation
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e91bc677e3778a7dd7f4dfca4b5b0d2d9d0ddeaa
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_119/search_replace.txt
@@ -0,0 +1,214 @@
+
+multi_strategy_mutation
+
+
+This edit replaces the simple, iterative Gaussian jitter mutation with a more powerful, vectorized, multi-strategy operator. The new `mutate` function combines three techniques, drawing inspiration from high-performing prior programs:
+
+1. **Vectorized Gaussian Jitter**: Efficiently applies small random perturbations for local fine-tuning.
+2. **Probabilistic Swap Mutation**: With a 10% chance, it swaps the positions of two random circles. This is a large-scale topological move that helps the algorithm explore fundamentally different packing structures.
+3. **Probabilistic Re-initialize Mutation**: With a 5% chance, it moves a single, randomly chosen circle to a new random location. This acts as a powerful mechanism to escape deep local optima where one circle might be poorly placed.
+
+This combination of operators provides a much healthier balance between local exploitation and global exploration, which is critical for navigating the complex search space and avoiding premature convergence.
+
+
+<<<<<<< SEARCH
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies simple Gaussian noise mutation."""
+ mutated_centers = individual.centers.copy()
+ for i in range(n_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0)
+ return individual
+=======
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies for better exploration:
+ 1. Standard Gaussian Jitter (local fine-tuning).
+ 2. Swap Mutation (topological change).
+ 3. Re-initialize Mutation (escape deep local optima).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ mutated_centers[mask] += noise[mask]
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+>>>>>>> REPLACE
+
+
+memetic_strategy_overhaul
+
+
+This edit overhauls the main Memetic Algorithm logic in `construct_packing` to significantly improve its search power and robustness, incorporating several best practices observed in prior high-performing programs.
+
+1. **Initial Population Optimization:** A crucial "memetic" step is added. The entire initial population is now optimized with the `HybridLocalSearcher` before the main evolutionary loop begins. This creates a strong starting base of locally-optimal individuals, dramatically accelerating convergence towards high-quality solutions.
+2. **Hyperparameter Tuning:** Key parameters (`POPULATION_SIZE`, `NUM_GENERATIONS`, `TOURNAMENT_SIZE`, `ELITISM_COUNT`, `LOCAL_SEARCH_PROB`, `INITIAL_MUTATION_STRENGTH`) are increased to support a more extensive and deeper search of the solution space.
+3. **Operator Chain Correction:** The order of operations in the main loop is changed from `Local Search -> Mutate` to the more effective `Mutate -> Local Search`. This allows the powerful local search to polish and refine the new solutions created by mutation.
+4. **Final Polishing:** A final, intensive local search with more simulation iterations is applied to the best overall solution found by the GA. This provides a final refinement step to maximize the packing density.
+5. **Code Safety:** The calls to the local searcher and the update of the best individual now use `.copy()`, preventing subtle bugs related to in-place modification of object attributes.
+
+
+<<<<<<< SEARCH
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 500
+ MUTATION_RATE = 0.20
+ INITIAL_MUTATION_STRENGTH = 0.05
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 5
+ ELITISM_COUNT = 5
+ LOCAL_SEARCH_PROB = 0.1 # Probability to apply the powerful local search
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Using default (tuned) params
+
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main GA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate new individuals
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search and Simple Mutation ---
+ # 1. Probabilistically apply the powerful but expensive local search
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers)
+
+ # 2. Always apply a small standard mutation for diversity
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Final high-precision evaluation
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+=======
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters ---
+ POPULATION_SIZE = 120 # Increased population for broader search
+ NUM_GENERATIONS = 800 # More generations for better convergence
+ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.08 # Larger initial mutations for exploration
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Preserve more of the best solutions
+ LOCAL_SEARCH_PROB = 0.20 # Increased probability of applying local search
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher()
+
+ # --- Crucial Memetic Step: Initial Local Optimization ---
+ # Apply local search to the entire initial population to create a strong starting gene pool
+ # of locally-optimal individuals. This is a key feature of successful memetic algorithms.
+ for individual in population:
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate new individuals
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Operator Chain: Mutate then (probabilistically) Local Search ---
+ # 1. Apply mutation to introduce variation
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # 2. Probabilistically apply local search to polish the new child
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a true copy to prevent the best solution from being modified by reference
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+
+ # Final polish on the best solution found with a more intensive local search
+ final_searcher = HybridLocalSearcher(sim_iter=600, fine_tune_iter=250, radius_sim_iter=200)
+ best_individual_overall.centers = final_searcher.optimize(best_individual_overall.centers.copy())
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..6acb108863d87f814f14851892639a9adc49a64f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/edit.diff
@@ -0,0 +1,161 @@
+--- a/original.py
++++ b/original.py
+@@ -1,155 +1,156 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250
+ learning_rate = 0.1
+ wall_repulsion_strength = 0.5
+
+ # --- Initialization ---
+ # Start with a perturbed grid. This is a good initial guess,
+ # and the perturbation helps break symmetry and escape a rigid structure.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+- # Place the 26th circle somewhere in the middle to start.
+- centers[25] = [0.51, 0.49]
++ # Place the 26th circle in a corner, slightly offset to allow for radius.
++ # This utilizes corner space efficiently.
++ centers[25] = [0.05, 0.05]
+
+ # Add small random noise to break grid symmetry
+ np.random.seed(42) # for reproducibility
+ centers += np.random.normal(0, 0.01, centers.shape)
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # --- Simulation Loop ---
+ for k in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap = radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a52f4e5cc497db1d4bec46fd65e2df4caf602d3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/main.py
@@ -0,0 +1,156 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250
+ learning_rate = 0.1
+ wall_repulsion_strength = 0.5
+
+ # --- Initialization ---
+ # Start with a perturbed grid. This is a good initial guess,
+ # and the perturbation helps break symmetry and escape a rigid structure.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in a corner, slightly offset to allow for radius.
+ # This utilizes corner space efficiently.
+ centers[25] = [0.05, 0.05]
+
+ # Add small random noise to break grid symmetry
+ np.random.seed(42) # for reproducibility
+ centers += np.random.normal(0, 0.01, centers.shape)
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # --- Simulation Loop ---
+ for k in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap = radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..77b52d2731c8af86d47d12f95a9efb6b960f9f0f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/original.py
@@ -0,0 +1,155 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250
+ learning_rate = 0.1
+ wall_repulsion_strength = 0.5
+
+ # --- Initialization ---
+ # Start with a perturbed grid. This is a good initial guess,
+ # and the perturbation helps break symmetry and escape a rigid structure.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle somewhere in the middle to start.
+ centers[25] = [0.51, 0.49]
+
+ # Add small random noise to break grid symmetry
+ np.random.seed(42) # for reproducibility
+ centers += np.random.normal(0, 0.01, centers.shape)
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # --- Simulation Loop ---
+ for k in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap = radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c0c0a4b12c12ff1bff763f7d420ff58e922cc5e8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_12/search_replace.txt
@@ -0,0 +1,18 @@
+
+corner_26th_circle
+
+
+
+The current placement of the 26th circle near the center results in a very small radius, indicating inefficient use of space. Higher-scoring previous programs initialized the 26th circle in a corner (e.g., `[0.05, 0.05]`), which allows it to grow larger and utilize valuable corner space that the main 5x5 grid doesn't fully cover. This change repositions the initial placement of the 26th circle to a corner, giving the simulation a better starting point to maximize radii. This aligns with the "Dynamic Placement of the 'Extra' Circle" recommendation.
+
+
+
+<<<<<<< SEARCH
+ # Place the 26th circle somewhere in the middle to start.
+ centers[25] = [0.51, 0.49]
+=======
+ # Place the 26th circle in a corner, slightly offset to allow for radius.
+ # This utilizes corner space efficiently.
+ centers[25] = [0.05, 0.05]
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6be852c080f9530f818b0ce8c757002c7f6cae0e
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..ffed2fa76adb1fabb80105e8c1a09d72c26267f3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/edit.diff
@@ -0,0 +1,487 @@
+--- a/original.py
++++ b/original.py
+@@ -1,315 +1,258 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
++import math
++import random
+
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ # Calculate distances and handle overlaps for all pairs
+ # Vectorized calculation for distances to improve performance
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+- # Check for convergence: if no updates were made AND the max change is tiny.
+- # This prevents infinite loops if changes are smaller than convergence_threshold
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+-class HybridLocalSearcher:
+- """
+- A powerful local search refiner based on a two-phase physical simulation.
+- This approach incorporates a "growth pressure" concept for exploration.
+- """
+- def __init__(self, n, config):
+- self.n = n
+- self.config = config
+-
+- def _compute_radii_for_sim(self, centers):
+- """A fast radius computation for use inside the simulation loop."""
+- return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
++class LightweightLocalSearcher:
++ """A fast local refiner, used as a move operator within SA."""
++ def __init__(self, config):
++ self.iterations = config['ls_iter']
++ self.lr = config['ls_lr']
++ self.wall_strength = config['ls_wall_strength']
++ self.radius_iter = config['ls_radius_iter']
+
+ def refine(self, centers):
+- """Applies a two-phase physical simulation to refine a configuration."""
++ """Applies a few steps of force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+-
+- # Phase 1: Main refinement with decreasing growth pressure
+- iterations = self.config['sim_iter']
+- base_lr = self.config['learning_rate']
+- wall_strength = self.config['wall_strength']
+- initial_growth_pressure = self.config['initial_growth_pressure']
+- final_growth_pressure = self.config['final_growth_pressure']
+-
+- for i_iter in range(iterations):
+- progress = i_iter / iterations
+- # Anneal growth pressure quadratically
+- current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+- current_radii = self._compute_radii_for_sim(refined_centers)
+- pressured_radii = current_radii * current_growth_pressure
+-
+- # Vectorized force calculations
+- diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+- radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+- overlaps = np.maximum(0, radii_sums - dists)
+- np.fill_diagonal(overlaps, 0)
+- force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- wall_forces = np.zeros_like(refined_centers)
+- # Wall forces calculation
+- wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+- wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+-
+- forces = circle_forces + wall_forces
+- # Anneal learning rate quadratically
+- lr = base_lr * (1.0 - progress)**2
+- refined_centers += forces * lr
+- refined_centers = np.clip(refined_centers, 0.0, 1.0)
+-
+- # Phase 2: Fine-tuning with minimal growth pressure
+- iterations_ft = self.config['fine_tune_iter']
+- lr_ft = self.config['fine_tune_lr']
+- for _ in range(iterations_ft):
+- current_radii = self._compute_radii_for_sim(refined_centers)
++ for _ in range(self.iterations):
++ radii = compute_max_radii(refined_centers, max_iter=self.radius_iter)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+- radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
++
++ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
++
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+- # Wall forces calculation
+- wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+- wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
++ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
++ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+- refined_centers += forces * lr_ft
++
++ refined_centers += forces * self.lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+-
++
+ return refined_centers
+
+
+-class MemeticAlgorithm:
+- """Encapsulates the entire Memetic Algorithm for circle packing."""
+- def __init__(self, n, config):
++class AdaptiveSimulatedAnnealer:
++ """
++ Performs SA with adaptive move selection, local search infusion, and reheating.
++ """
++ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+- self.population = []
+- self.fitnesses = np.array([])
+- self.best_solution = None
+- self.best_fitness = -1.0
+- # Use the HybridLocalSearcher with its specific config
+- self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+-
+- def _initialize_population(self):
+- """Initializes a diverse population using strategic grid-based starts and random individuals."""
+- spacing = 1.0 / 5
+- # The explicit list of candidate positions for the 26th circle, from a high-performing prior
+- candidate_extra_positions = self.config['initial_candidates']
+-
+- # Base for 25 circles in a 5x5 grid, to be perturbed
+- base_centers_25 = np.zeros((self.n - 1, 2))
+- k = 0
+- for j in range(5):
+- for i in range(5):
+- base_centers_25[k, 0] = (i + 0.5) * spacing
+- base_centers_25[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+-
+- # Add strategically initialized configurations
+- for i in range(num_strategic_starts):
+- centers = np.zeros((self.n, 2))
+- # Apply perturbation to break symmetry
+- perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers_25.shape)
+- perturbed_base_centers = np.clip(base_centers_25 + perturbation, 0.0, 1.0)
+- centers[:self.n-1] = perturbed_base_centers
+- # Place the 26th circle at a strategic point
+- centers[self.n-1] = np.array(candidate_extra_positions[i])
+- self.population.append(centers)
+-
+- # Add purely random configurations to ensure broad exploration
+- num_random_starts = self.config['population_size'] - len(self.population)
+- for _ in range(num_random_starts):
+- self.population.append(np.random.rand(self.n, 2))
+-
+- def _evaluate_population(self):
+- """Calculates fitness for the population and updates the best solution."""
+- self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+- best_idx = np.argmax(self.fitnesses)
+- if self.fitnesses[best_idx] > self.best_fitness:
+- self.best_fitness = self.fitnesses[best_idx]
+- self.best_solution = self.population[best_idx].copy()
+-
+- def _select_parent(self):
+- """Selects a parent using tournament selection."""
+- tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+- tourn_fitnesses = self.fitnesses[tourn_indices]
+- winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+- return self.population[winner_idx]
+-
+- def _crossover(self, p1, p2):
+- """Performs uniform crossover, taking whole circles from either parent."""
+- child = p1.copy()
+- mask = np.random.rand(self.n) < 0.5
+- child[mask] = p2[mask]
+- return child
+-
+- def _mutate(self, individual, strength, mutation_rate_per_circle):
+- """Applies Gaussian mutation and a chance of a strong 'jump' mutation."""
+- mutated_ind = individual.copy()
+- for i in range(self.n):
+- if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+- noise = np.random.normal(0, strength, size=2)
+- mutated_ind[i] += noise
+-
+- if np.random.rand() < self.config['jump_mutation_prob']: # Jump mutation probability from a good prior
+- idx_to_reset = np.random.randint(0, self.n)
+- mutated_ind[idx_to_reset] = np.random.rand(2)
+-
+- return np.clip(mutated_ind, 0.0, 1.0)
++ self.centers = initial_centers.copy()
++
++ self.temp = config['t_start']
++ self.max_step_size = config['max_step_size']
++
++ self.energy = self._calculate_energy(self.centers)
++ self.best_centers = self.centers.copy()
++ self.best_energy = self.energy
++
++ self.initial_move_weights = np.array(config['initial_move_weights'])
++ self.final_move_weights = np.array(config['final_move_weights'])
++
++ self.local_searcher = LightweightLocalSearcher(config=config['ls_config'])
++ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles, self._local_search_refine]
++
++ self.no_improvement_counter = 0
++
++ def _calculate_energy(self, centers):
++ """Energy is the negative sum of radii, to be minimized."""
++ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
++ return -np.sum(radii)
++
++ def _propose_move(self):
++ """Proposes a new state by selecting a move type and applying it."""
++ t_progress = (self.config['t_start'] - self.temp) / (self.config['t_start'] - self.config['t_end'])
++
++ current_move_weights = self.initial_move_weights * (1 - t_progress) + self.final_move_weights * t_progress
++ move_func = random.choices(self.move_types, weights=current_move_weights, k=1)[0]
++
++ current_step_size = self.max_step_size * (self.temp / self.config['t_start'])**0.75
++
++ return move_func(current_step_size)
++
++ def _move_single_circle(self, step_size):
++ new_centers = self.centers.copy()
++ idx = random.randint(0, self.n - 1)
++ displacement = np.random.normal(0, step_size, size=2)
++ new_centers[idx] += displacement
++ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
++ return new_centers
++
++ def _move_cluster(self, step_size):
++ new_centers = self.centers.copy()
++ seed_idx = random.randint(0, self.n - 1)
++ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
++ neighbor_indices = np.argsort(dists)[:self.config['cluster_size']]
++ displacement = np.random.normal(0, step_size * 0.5, size=2)
++ new_centers[neighbor_indices] += displacement
++ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
++ return new_centers
++
++ def _swap_circles(self, step_size):
++ new_centers = self.centers.copy()
++ if self.n < 2: return new_centers
++ idx1, idx2 = random.sample(range(self.n), 2)
++ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
++ return new_centers
++
++ def _local_search_refine(self, step_size): # step_size is ignored
++ return self.local_searcher.refine(self.centers)
+
+ def run(self):
+- """Executes the full memetic algorithm evolution."""
+- self._initialize_population()
+-
+- for gen in range(self.config['generations']):
+- self._evaluate_population()
+-
+- new_population = []
+-
+- # Elitism: carry over the best individuals directly
+- elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+- for idx in elite_indices:
+- new_population.append(self.population[idx].copy())
+-
+- # Anneal mutation strength over generations with a quadratic decay
+- mut_strength = self.config['mut_strength_end'] + \
+- (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+- (1.0 - (gen / self.config['generations']))**2.0
+-
+- # Anneal mutation rate per circle over generations with a cubic decay
+- current_mutation_rate_per_circle = self.config['mutation_rate_end_per_circle'] + \
+- (self.config['mutation_rate_start_per_circle'] - self.config['mutation_rate_end_per_circle']) * \
+- (1.0 - (gen / self.config['generations']))**1.5
+-
+- while len(new_population) < self.config['population_size']:
+- p1 = self._select_parent()
+- p2 = self._select_parent()
++ """Executes the simulated annealing search."""
++ while self.temp > self.config['t_end']:
++ for _ in range(self.config['moves_per_temp']):
++ new_centers = self._propose_move()
++ new_energy = self._calculate_energy(new_centers)
++ delta_e = new_energy - self.energy
+
+- # Perform crossover, or just copy a parent if crossover doesn't happen
+- child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+- # Apply mutation with annealed strength and rate
+- child = self._mutate(child, mut_strength, current_mutation_rate_per_circle)
+-
+- # Memetic step: Apply powerful local search probabilistically
+- if np.random.rand() < self.config['local_search_prob']:
+- child = self.local_search_refiner.refine(child)
+-
+- new_population.append(child)
+-
+- self.population = new_population
+-
+- # Final evaluation to get the best of the last generation
+- self._evaluate_population()
+- return self.best_solution
++ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
++ self.centers = new_centers
++ self.energy = new_energy
++
++ if self.energy < self.best_energy:
++ self.best_energy = self.energy
++ self.best_centers = self.centers.copy()
++ self.no_improvement_counter = 0
++
++ self.temp *= self.config['cooling_rate']
++ self.no_improvement_counter += 1
++
++ if self.config['reheat_enabled'] and self.no_improvement_counter > self.config['reheat_patience']:
++ self.temp = self.config['reheat_temp']
++ self.no_improvement_counter = 0
++ self.centers = self.best_centers + np.random.normal(0, 0.01, self.centers.shape)
++ self.centers = np.clip(self.centers, 0, 1)
++ self.energy = self._calculate_energy(self.centers)
++
++ return self.best_centers
+
+
+ def construct_packing():
+- """
+- Constructs a packing of 26 circles using a Memetic Algorithm.
+- """
++ """Constructs a packing using a multi-start, adaptive SA with memetic infusion."""
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+- # MA parameters
+- 'population_size': 80, # Increased population size for more diversity
+- 'generations': 350, # Sufficient generations for thorough evolution
+- 'elite_count': 5, # Number of elite individuals to carry over
+- 'tournament_size': 6, # Tournament size for selection pressure
+- 'mutation_rate_start_per_circle': 0.45, # Initial probability for a circle to mutate
+- 'mutation_rate_end_per_circle': 0.05, # Final probability after annealing
+- 'mut_strength_start': 0.1, # Initial strength of Gaussian mutation
+- 'mut_strength_end': 0.001, # Final strength of Gaussian mutation
+- 'crossover_rate': 0.9, # Probability of performing crossover
+- 'jump_mutation_prob': 0.03, # Probability of a strong "jump" mutation for a circle
+-
+- # Initialization parameters for _initialize_population
+- 'initial_perturbation_scale': 0.02, # Scale for perturbing grid-based initial positions
+- 'initial_candidates': [ # Strategic points for the 26th circle
+- [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+- [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
++ 'num_starts': 16,
++ 't_start': 0.05,
++ 't_end': 1e-7,
++ 'cooling_rate': 0.9995,
++ 'moves_per_temp': 100,
++ 'max_step_size': 0.20,
++ 'radius_iter': 250,
++ 'cluster_size': 5,
++ 'initial_move_weights': [0.60, 0.20, 0.15, 0.05], # single, cluster, swap, LS
++ 'final_move_weights': [0.60, 0.05, 0.05, 0.30],
++ 'reheat_enabled': True,
++ 'reheat_patience': 250,
++ 'reheat_temp': 0.02,
++
++ 'ls_config': {
++ 'ls_iter': 75,
++ 'ls_lr': 0.008,
++ 'ls_wall_strength': 0.5,
++ 'ls_radius_iter': 100,
++ },
++ 'initial_candidates': [
++ [spacing, spacing], [spacing, 1-spacing], [1-spacing, spacing], [1-spacing, 1-spacing],
++ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1-spacing, 0.5], [0.5, 1-spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+-
+- # Memetic Parameters (Local Search, passed to HybridLocalSearcher)
+- 'local_search_prob': 0.20, # Probability of applying local search to a new child
+- 'ls_config': { # Configuration for the HybridLocalSearcher
+- 'sim_iter': 500, # Iterations for the main simulation phase
+- 'radius_sim_iter': 150, # Radii calculation iterations during simulation
+- 'learning_rate': 0.018, # Base learning rate for center updates
+- 'wall_strength': 0.65, # Strength of repulsion from square walls
+- 'initial_growth_pressure': 1.05, # Initial multiplier for radii to create expansion pressure
+- 'final_growth_pressure': 1.001, # Final multiplier for radii
+- 'fine_tune_iter': 250, # Iterations for the fine-tuning phase
+- 'fine_tune_lr': 0.0015, # Learning rate for the fine-tuning phase
+- }
+ }
+
+- solver = MemeticAlgorithm(n=n, config=config)
+- best_centers = solver.run()
+-
+- # Final, high-precision radius calculation for the best solution
+- final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
+-
+- return best_centers, final_radii
++ best_overall_centers = None
++ best_overall_score = -1.0
++
++ for i in range(config['num_starts']):
++ initial_centers = np.zeros((n, 2))
++ if i < len(config['initial_candidates']):
++ base_centers_25 = np.zeros((n - 1, 2))
++ k = 0
++ for j in range(5):
++ for row_i in range(5):
++ base_centers_25[k, 0] = (row_i + 0.5) * spacing
++ base_centers_25[k, 1] = (j + 0.5) * spacing
++ k += 1
++ initial_centers[:n-1] = base_centers_25 + np.random.normal(0, 0.02, size=(n-1, 2))
++ initial_centers[n-1] = np.array(config['initial_candidates'][i])
++ initial_centers = np.clip(initial_centers, 0.0, 1.0)
++ else:
++ initial_centers = np.random.rand(n, 2)
++
++ solver = AdaptiveSimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
++ result_centers = solver.run()
++
++ radii = compute_max_radii(result_centers, max_iter=2500)
++ score = np.sum(radii)
++
++ if score > best_overall_score:
++ best_overall_score = score
++ best_overall_centers = result_centers
++
++ final_radii = compute_max_radii(best_overall_centers, max_iter=3000, convergence_threshold=1e-9)
++ return best_overall_centers, final_radii
++
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f9384869c42c905627327fd402eabf353f8f59c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/main.py
@@ -0,0 +1,258 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ # Calculate distances and handle overlaps for all pairs
+ # Vectorized calculation for distances to improve performance
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LightweightLocalSearcher:
+ """A fast local refiner, used as a move operator within SA."""
+ def __init__(self, config):
+ self.iterations = config['ls_iter']
+ self.lr = config['ls_lr']
+ self.wall_strength = config['ls_wall_strength']
+ self.radius_iter = config['ls_radius_iter']
+
+ def refine(self, centers):
+ """Applies a few steps of force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ for _ in range(self.iterations):
+ radii = compute_max_radii(refined_centers, max_iter=self.radius_iter)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class AdaptiveSimulatedAnnealer:
+ """
+ Performs SA with adaptive move selection, local search infusion, and reheating.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers.copy()
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.initial_move_weights = np.array(config['initial_move_weights'])
+ self.final_move_weights = np.array(config['final_move_weights'])
+
+ self.local_searcher = LightweightLocalSearcher(config=config['ls_config'])
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles, self._local_search_refine]
+
+ self.no_improvement_counter = 0
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """Proposes a new state by selecting a move type and applying it."""
+ t_progress = (self.config['t_start'] - self.temp) / (self.config['t_start'] - self.config['t_end'])
+
+ current_move_weights = self.initial_move_weights * (1 - t_progress) + self.final_move_weights * t_progress
+ move_func = random.choices(self.move_types, weights=current_move_weights, k=1)[0]
+
+ current_step_size = self.max_step_size * (self.temp / self.config['t_start'])**0.75
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ new_centers = self.centers.copy()
+ seed_idx = random.randint(0, self.n - 1)
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:self.config['cluster_size']]
+ displacement = np.random.normal(0, step_size * 0.5, size=2)
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def _local_search_refine(self, step_size): # step_size is ignored
+ return self.local_searcher.refine(self.centers)
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ new_centers = self._propose_move()
+ new_energy = self._calculate_energy(new_centers)
+ delta_e = new_energy - self.energy
+
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+ self.no_improvement_counter = 0
+
+ self.temp *= self.config['cooling_rate']
+ self.no_improvement_counter += 1
+
+ if self.config['reheat_enabled'] and self.no_improvement_counter > self.config['reheat_patience']:
+ self.temp = self.config['reheat_temp']
+ self.no_improvement_counter = 0
+ self.centers = self.best_centers + np.random.normal(0, 0.01, self.centers.shape)
+ self.centers = np.clip(self.centers, 0, 1)
+ self.energy = self._calculate_energy(self.centers)
+
+ return self.best_centers
+
+
+def construct_packing():
+ """Constructs a packing using a multi-start, adaptive SA with memetic infusion."""
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ 'num_starts': 16,
+ 't_start': 0.05,
+ 't_end': 1e-7,
+ 'cooling_rate': 0.9995,
+ 'moves_per_temp': 100,
+ 'max_step_size': 0.20,
+ 'radius_iter': 250,
+ 'cluster_size': 5,
+ 'initial_move_weights': [0.60, 0.20, 0.15, 0.05], # single, cluster, swap, LS
+ 'final_move_weights': [0.60, 0.05, 0.05, 0.30],
+ 'reheat_enabled': True,
+ 'reheat_patience': 250,
+ 'reheat_temp': 0.02,
+
+ 'ls_config': {
+ 'ls_iter': 75,
+ 'ls_lr': 0.008,
+ 'ls_wall_strength': 0.5,
+ 'ls_radius_iter': 100,
+ },
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1-spacing], [1-spacing, spacing], [1-spacing, 1-spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1-spacing, 0.5], [0.5, 1-spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ for i in range(config['num_starts']):
+ initial_centers = np.zeros((n, 2))
+ if i < len(config['initial_candidates']):
+ base_centers_25 = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(5):
+ for row_i in range(5):
+ base_centers_25[k, 0] = (row_i + 0.5) * spacing
+ base_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+ initial_centers[:n-1] = base_centers_25 + np.random.normal(0, 0.02, size=(n-1, 2))
+ initial_centers[n-1] = np.array(config['initial_candidates'][i])
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ initial_centers = np.random.rand(n, 2)
+
+ solver = AdaptiveSimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ radii = compute_max_radii(result_centers, max_iter=2500)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ final_radii = compute_max_radii(best_overall_centers, max_iter=3000, convergence_threshold=1e-9)
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..318cf111d430fb99c72101c10e3134b858a63013
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/original.py
@@ -0,0 +1,315 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ # Calculate distances and handle overlaps for all pairs
+ # Vectorized calculation for distances to improve performance
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ # This prevents infinite loops if changes are smaller than convergence_threshold
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This approach incorporates a "growth pressure" concept for exploration.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ # Wall forces calculation
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ # Anneal learning rate quadratically
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ # Wall forces calculation
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ # Use the HybridLocalSearcher with its specific config
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
+ spacing = 1.0 / 5
+ # The explicit list of candidate positions for the 26th circle, from a high-performing prior
+ candidate_extra_positions = self.config['initial_candidates']
+
+ # Base for 25 circles in a 5x5 grid, to be perturbed
+ base_centers_25 = np.zeros((self.n - 1, 2))
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ base_centers_25[k, 0] = (i + 0.5) * spacing
+ base_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+
+ # Add strategically initialized configurations
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Apply perturbation to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers_25.shape)
+ perturbed_base_centers = np.clip(base_centers_25 + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Place the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # Add purely random configurations to ensure broad exploration
+ num_random_starts = self.config['population_size'] - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong 'jump' mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']: # Jump mutation probability from a good prior
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals directly
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength over generations with a quadratic decay
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ # Anneal mutation rate per circle over generations with a cubic decay
+ current_mutation_rate_per_circle = self.config['mutation_rate_end_per_circle'] + \
+ (self.config['mutation_rate_start_per_circle'] - self.config['mutation_rate_end_per_circle']) * \
+ (1.0 - (gen / self.config['generations']))**1.5
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ # Perform crossover, or just copy a parent if crossover doesn't happen
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ # Apply mutation with annealed strength and rate
+ child = self._mutate(child, mut_strength, current_mutation_rate_per_circle)
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 350, # Sufficient generations for thorough evolution
+ 'elite_count': 5, # Number of elite individuals to carry over
+ 'tournament_size': 6, # Tournament size for selection pressure
+ 'mutation_rate_start_per_circle': 0.45, # Initial probability for a circle to mutate
+ 'mutation_rate_end_per_circle': 0.05, # Final probability after annealing
+ 'mut_strength_start': 0.1, # Initial strength of Gaussian mutation
+ 'mut_strength_end': 0.001, # Final strength of Gaussian mutation
+ 'crossover_rate': 0.9, # Probability of performing crossover
+ 'jump_mutation_prob': 0.03, # Probability of a strong "jump" mutation for a circle
+
+ # Initialization parameters for _initialize_population
+ 'initial_perturbation_scale': 0.02, # Scale for perturbing grid-based initial positions
+ 'initial_candidates': [ # Strategic points for the 26th circle
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search, passed to HybridLocalSearcher)
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ 'ls_config': { # Configuration for the HybridLocalSearcher
+ 'sim_iter': 500, # Iterations for the main simulation phase
+ 'radius_sim_iter': 150, # Radii calculation iterations during simulation
+ 'learning_rate': 0.018, # Base learning rate for center updates
+ 'wall_strength': 0.65, # Strength of repulsion from square walls
+ 'initial_growth_pressure': 1.05, # Initial multiplier for radii to create expansion pressure
+ 'final_growth_pressure': 1.001, # Final multiplier for radii
+ 'fine_tune_iter': 250, # Iterations for the fine-tuning phase
+ 'fine_tune_lr': 0.0015, # Learning rate for the fine-tuning phase
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..347cd517e0ec7dc5d9baeaf2bccb0258e0a37a05
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/results/job_log.err
@@ -0,0 +1,25 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
+Traceback (most recent call last):
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/evaluate.py", line 302, in
+ main(parsed_args.program_path, parsed_args.results_dir)
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/evaluate.py", line 262, in main
+ metrics, correct, error_msg = run_shinka_eval(
+ ^^^^^^^^^^^^^^^^
+ File "/home/tengxiao/pj/ShinkaEvolve/shinka/core/wrap_eval.py", line 119, in run_shinka_eval
+ run_result = experiment_fn(**kwargs)
+ ^^^^^^^^^^^^^^^^^^^^^^^
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/main.py", line 255, in run_packing
+ centers, radii = construct_packing()
+ ^^^^^^^^^^^^^^^^^^^
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/main.py", line 237, in construct_packing
+ result_centers = solver.run()
+ ^^^^^^^^^^^^
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/main.py", line 158, in run
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..637b8d7cf4e90de3fb2a9da12b8be5926c7eaca4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/results/job_log.out
@@ -0,0 +1,2 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/results
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3342de34e9a48a39491d704e4e68c0016e770780
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_120/rewrite.txt
@@ -0,0 +1,249 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ # Calculate distances and handle overlaps for all pairs
+ # Vectorized calculation for distances to improve performance
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LightweightLocalSearcher:
+ """A fast local refiner, used as a move operator within SA."""
+ def __init__(self, config):
+ self.iterations = config['ls_iter']
+ self.lr = config['ls_lr']
+ self.wall_strength = config['ls_wall_strength']
+ self.radius_iter = config['ls_radius_iter']
+
+ def refine(self, centers):
+ """Applies a few steps of force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ for _ in range(self.iterations):
+ radii = compute_max_radii(refined_centers, max_iter=self.radius_iter)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class AdaptiveSimulatedAnnealer:
+ """
+ Performs SA with adaptive move selection, local search infusion, and reheating.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers.copy()
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.initial_move_weights = np.array(config['initial_move_weights'])
+ self.final_move_weights = np.array(config['final_move_weights'])
+
+ self.local_searcher = LightweightLocalSearcher(config=config['ls_config'])
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles, self._local_search_refine]
+
+ self.no_improvement_counter = 0
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """Proposes a new state by selecting a move type and applying it."""
+ t_progress = (self.config['t_start'] - self.temp) / (self.config['t_start'] - self.config['t_end'])
+
+ current_move_weights = self.initial_move_weights * (1 - t_progress) + self.final_move_weights * t_progress
+ move_func = random.choices(self.move_types, weights=current_move_weights, k=1)[0]
+
+ current_step_size = self.max_step_size * (self.temp / self.config['t_start'])**0.75
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ new_centers = self.centers.copy()
+ seed_idx = random.randint(0, self.n - 1)
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:self.config['cluster_size']]
+ displacement = np.random.normal(0, step_size * 0.5, size=2)
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def _local_search_refine(self, step_size): # step_size is ignored
+ return self.local_searcher.refine(self.centers)
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ new_centers = self._propose_move()
+ new_energy = self._calculate_energy(new_centers)
+ delta_e = new_energy - self.energy
+
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+ self.no_improvement_counter = 0
+
+ self.temp *= self.config['cooling_rate']
+ self.no_improvement_counter += 1
+
+ if self.config['reheat_enabled'] and self.no_improvement_counter > self.config['reheat_patience']:
+ self.temp = self.config['reheat_temp']
+ self.no_improvement_counter = 0
+ self.centers = self.best_centers + np.random.normal(0, 0.01, self.centers.shape)
+ self.centers = np.clip(self.centers, 0, 1)
+ self.energy = self._calculate_energy(self.centers)
+
+ return self.best_centers
+
+
+def construct_packing():
+ """Constructs a packing using a multi-start, adaptive SA with memetic infusion."""
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ 'num_starts': 16,
+ 't_start': 0.05,
+ 't_end': 1e-7,
+ 'cooling_rate': 0.9995,
+ 'moves_per_temp': 100,
+ 'max_step_size': 0.20,
+ 'radius_iter': 250,
+ 'cluster_size': 5,
+ 'initial_move_weights': [0.60, 0.20, 0.15, 0.05], # single, cluster, swap, LS
+ 'final_move_weights': [0.60, 0.05, 0.05, 0.30],
+ 'reheat_enabled': True,
+ 'reheat_patience': 250,
+ 'reheat_temp': 0.02,
+
+ 'ls_config': {
+ 'ls_iter': 75,
+ 'ls_lr': 0.008,
+ 'ls_wall_strength': 0.5,
+ 'ls_radius_iter': 100,
+ },
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1-spacing], [1-spacing, spacing], [1-spacing, 1-spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1-spacing, 0.5], [0.5, 1-spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ for i in range(config['num_starts']):
+ initial_centers = np.zeros((n, 2))
+ if i < len(config['initial_candidates']):
+ base_centers_25 = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(5):
+ for row_i in range(5):
+ base_centers_25[k, 0] = (row_i + 0.5) * spacing
+ base_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+ initial_centers[:n-1] = base_centers_25 + np.random.normal(0, 0.02, size=(n-1, 2))
+ initial_centers[n-1] = np.array(config['initial_candidates'][i])
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ initial_centers = np.random.rand(n, 2)
+
+ solver = AdaptiveSimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ radii = compute_max_radii(result_centers, max_iter=2500)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ final_radii = compute_max_radii(best_overall_centers, max_iter=3000, convergence_threshold=1e-9)
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..1d1846f0e324c88871b1c8e4f105a399ac560393
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/edit.diff
@@ -0,0 +1,386 @@
+--- a/original.py
++++ b/original.py
+@@ -1,360 +1,377 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ # --- Core Utility: Radius Calculation ---
+ def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+ # --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+ class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+ # Using a copy to avoid modifying the original array if `centers` is passed by reference
+ current_centers = centers.copy()
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(current_centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ current_centers += forces * lr
+ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(current_centers, radii)
+ current_centers += forces * self.fine_tune_lr
+ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
+
+ return current_centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+ # --- GA Core Components ---
+ class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+ def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # --- Strategy: Grid + Extra Point Variations ---
+ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
+ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate positions for the 26th circle
+ extra_pos_candidates = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # Center
+ [0.05, 0.05], [0.95, 0.05], # Near corners
+ [0.05, 0.95], [0.95, 0.95],
+ [0.5, 0.1], [0.1, 0.5], # Near edge midpoints
+ [0.5, 0.9], [0.9, 0.5],
+ [0.25, 0.25], [0.75, 0.75], # Diagonal
+ [0.25, 0.75], [0.75, 0.25],
+ [0.3, 0.1], [0.7, 0.1], # more points
+ [0.1, 0.3], [0.1, 0.7]
+ ]
+
+ # Generate initial individuals from these grid configurations
+ # Aim for about 1/3 to 1/2 of population from grids
+ num_grid_starts = min(population_size // 2, len(extra_pos_candidates) * 2)
+
+ for i in range(num_grid_starts):
+ current_centers = np.zeros((n_circles, 2))
+ current_centers[:n_circles-1] = base_grid_centers_25.copy()
+
+ # Select an extra position candidate, cycle through them
+ extra_pos = extra_pos_candidates[i % len(extra_pos_candidates)]
+ current_centers[n_circles-1] = np.array(extra_pos)
+
+ # Apply a small perturbation to the grid and the extra circle
+ perturbation_strength = 0.018 # Slightly increased perturbation
+ current_centers += np.random.normal(0, perturbation_strength, (n_circles, 2))
+
+ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
+
+ # --- Strategy: Completely Random Starts ---
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+ def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+ def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+ def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+- Applies Gaussian noise mutation to individual circle centers.
+- Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+- perturbed by Gaussian noise with `mutation_strength` standard deviation.
++ Applies an enhanced mutation operator with multiple strategies:
++ 1. Standard Gaussian Jitter (for local fine-tuning).
++ 2. Swap Mutation (for large-scale topological changes).
++ 3. Re-initialize Mutation (to escape from poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+- mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+-
+- # Apply Gaussian noise only to selected circles
++
++ # --- Strategy 1: Standard Gaussian Jitter ---
++ # Apply small perturbations for fine-tuning the positions.
++ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
++
++ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
++ # Swaps the positions of two circles. This is a powerful topological operator
++ # that can significantly alter the packing structure and help escape local optima.
++ if np.random.rand() < 0.10: # 10% chance to swap two random circles
++ if n_circles > 1:
++ i, j = np.random.choice(n_circles, 2, replace=False)
++ mutated_centers[[i, j]] = mutated_centers[[j, i]]
++
++ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
++ # Moves a single circle to a completely new random position. This helps to
++ # rescue circles that are "trapped" in a very suboptimal location.
++ if np.random.rand() < 0.05: # 5% chance to re-initialize one random circle
++ idx_to_reinit = np.random.randint(0, n_circles)
++ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+ # --- Main Orchestrator: Memetic Algorithm ---
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 120 # Increased population size for more diversity
+ NUM_GENERATIONS = 800 # Increased generations for longer evolution
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.25 # Probability to apply the powerful local search to a new child
+
+ # Radius calculation iterations: lower for speed during GA, higher for final precision
+ GA_RADIUS_ITER = 80 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # Local Search parameters for within the GA loop (lighter, faster iterations)
+ ga_local_searcher = HybridLocalSearcher(
+ sim_iter=200, fine_tune_iter=70, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.04,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=100
+ )
+
+ # Local Search parameters for the final polish (more intensive, run once)
+ final_local_searcher = HybridLocalSearcher(
+ sim_iter=800, fine_tune_iter=250, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=200
+ )
+
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Crucial: Initial Local Optimization of Entire Population (from Prior Program G97)
+ # Applying a light local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ for individual in population:
+ individual.centers = ga_local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = ga_local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Important: copy the individual to ensure we don't modify it later if it's part of the new population
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+ best_individual_overall.radii = current_best_in_gen.radii # Also copy radii for consistency
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final, intensive local search on the overall best individual
+ best_individual_overall.centers = final_local_searcher.optimize(best_individual_overall.centers.copy())
+ # Final high-precision evaluation
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..cabcff436d80b0f5ddb24ac1ade4bfdaff23dba4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/main.py
@@ -0,0 +1,377 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+ # Using a copy to avoid modifying the original array if `centers` is passed by reference
+ current_centers = centers.copy()
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(current_centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ current_centers += forces * lr
+ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(current_centers, radii)
+ current_centers += forces * self.fine_tune_lr
+ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
+
+ return current_centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # --- Strategy: Grid + Extra Point Variations ---
+ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
+ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate positions for the 26th circle
+ extra_pos_candidates = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # Center
+ [0.05, 0.05], [0.95, 0.05], # Near corners
+ [0.05, 0.95], [0.95, 0.95],
+ [0.5, 0.1], [0.1, 0.5], # Near edge midpoints
+ [0.5, 0.9], [0.9, 0.5],
+ [0.25, 0.25], [0.75, 0.75], # Diagonal
+ [0.25, 0.75], [0.75, 0.25],
+ [0.3, 0.1], [0.7, 0.1], # more points
+ [0.1, 0.3], [0.1, 0.7]
+ ]
+
+ # Generate initial individuals from these grid configurations
+ # Aim for about 1/3 to 1/2 of population from grids
+ num_grid_starts = min(population_size // 2, len(extra_pos_candidates) * 2)
+
+ for i in range(num_grid_starts):
+ current_centers = np.zeros((n_circles, 2))
+ current_centers[:n_circles-1] = base_grid_centers_25.copy()
+
+ # Select an extra position candidate, cycle through them
+ extra_pos = extra_pos_candidates[i % len(extra_pos_candidates)]
+ current_centers[n_circles-1] = np.array(extra_pos)
+
+ # Apply a small perturbation to the grid and the extra circle
+ perturbation_strength = 0.018 # Slightly increased perturbation
+ current_centers += np.random.normal(0, perturbation_strength, (n_circles, 2))
+
+ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
+
+ # --- Strategy: Completely Random Starts ---
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter ---
+ # Apply small perturbations for fine-tuning the positions.
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ # Swaps the positions of two circles. This is a powerful topological operator
+ # that can significantly alter the packing structure and help escape local optima.
+ if np.random.rand() < 0.10: # 10% chance to swap two random circles
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ mutated_centers[[i, j]] = mutated_centers[[j, i]]
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ # Moves a single circle to a completely new random position. This helps to
+ # rescue circles that are "trapped" in a very suboptimal location.
+ if np.random.rand() < 0.05: # 5% chance to re-initialize one random circle
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 120 # Increased population size for more diversity
+ NUM_GENERATIONS = 800 # Increased generations for longer evolution
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.25 # Probability to apply the powerful local search to a new child
+
+ # Radius calculation iterations: lower for speed during GA, higher for final precision
+ GA_RADIUS_ITER = 80 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # Local Search parameters for within the GA loop (lighter, faster iterations)
+ ga_local_searcher = HybridLocalSearcher(
+ sim_iter=200, fine_tune_iter=70, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.04,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=100
+ )
+
+ # Local Search parameters for the final polish (more intensive, run once)
+ final_local_searcher = HybridLocalSearcher(
+ sim_iter=800, fine_tune_iter=250, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=200
+ )
+
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Crucial: Initial Local Optimization of Entire Population (from Prior Program G97)
+ # Applying a light local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ for individual in population:
+ individual.centers = ga_local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = ga_local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Important: copy the individual to ensure we don't modify it later if it's part of the new population
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+ best_individual_overall.radii = current_best_in_gen.radii # Also copy radii for consistency
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final, intensive local search on the overall best individual
+ best_individual_overall.centers = final_local_searcher.optimize(best_individual_overall.centers.copy())
+ # Final high-precision evaluation
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d250c5e14205e9331c34d881ca82894734a32cac
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/original.py
@@ -0,0 +1,360 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+ # Using a copy to avoid modifying the original array if `centers` is passed by reference
+ current_centers = centers.copy()
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(current_centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ current_centers += forces * lr
+ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(current_centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(current_centers, radii)
+ current_centers += forces * self.fine_tune_lr
+ current_centers = np.clip(current_centers, 0.0, 1.0) # Keep centers within unit square
+
+ return current_centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # --- Strategy: Grid + Extra Point Variations ---
+ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
+ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate positions for the 26th circle
+ extra_pos_candidates = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # Center
+ [0.05, 0.05], [0.95, 0.05], # Near corners
+ [0.05, 0.95], [0.95, 0.95],
+ [0.5, 0.1], [0.1, 0.5], # Near edge midpoints
+ [0.5, 0.9], [0.9, 0.5],
+ [0.25, 0.25], [0.75, 0.75], # Diagonal
+ [0.25, 0.75], [0.75, 0.25],
+ [0.3, 0.1], [0.7, 0.1], # more points
+ [0.1, 0.3], [0.1, 0.7]
+ ]
+
+ # Generate initial individuals from these grid configurations
+ # Aim for about 1/3 to 1/2 of population from grids
+ num_grid_starts = min(population_size // 2, len(extra_pos_candidates) * 2)
+
+ for i in range(num_grid_starts):
+ current_centers = np.zeros((n_circles, 2))
+ current_centers[:n_circles-1] = base_grid_centers_25.copy()
+
+ # Select an extra position candidate, cycle through them
+ extra_pos = extra_pos_candidates[i % len(extra_pos_candidates)]
+ current_centers[n_circles-1] = np.array(extra_pos)
+
+ # Apply a small perturbation to the grid and the extra circle
+ perturbation_strength = 0.018 # Slightly increased perturbation
+ current_centers += np.random.normal(0, perturbation_strength, (n_circles, 2))
+
+ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
+
+ # --- Strategy: Completely Random Starts ---
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies Gaussian noise mutation to individual circle centers.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+
+ # Apply Gaussian noise only to selected circles
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 120 # Increased population size for more diversity
+ NUM_GENERATIONS = 800 # Increased generations for longer evolution
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.25 # Probability to apply the powerful local search to a new child
+
+ # Radius calculation iterations: lower for speed during GA, higher for final precision
+ GA_RADIUS_ITER = 80 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # Local Search parameters for within the GA loop (lighter, faster iterations)
+ ga_local_searcher = HybridLocalSearcher(
+ sim_iter=200, fine_tune_iter=70, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.04,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=100
+ )
+
+ # Local Search parameters for the final polish (more intensive, run once)
+ final_local_searcher = HybridLocalSearcher(
+ sim_iter=800, fine_tune_iter=250, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=200
+ )
+
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Crucial: Initial Local Optimization of Entire Population (from Prior Program G97)
+ # Applying a light local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ for individual in population:
+ individual.centers = ga_local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = ga_local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Important: copy the individual to ensure we don't modify it later if it's part of the new population
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+ best_individual_overall.radii = current_best_in_gen.radii # Also copy radii for consistency
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final, intensive local search on the overall best individual
+ best_individual_overall.centers = final_local_searcher.optimize(best_individual_overall.centers.copy())
+ # Final high-precision evaluation
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2e42719709247446cfbead05022a6e2db496ec69
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_121/search_replace.txt
@@ -0,0 +1,65 @@
+
+reinstate_advanced_mutation
+
+
+The current program's score has regressed compared to prior versions, and the visualization reveals "trapped" circles with minimal radii, a clear sign of the search stalling in a poor local optimum. This indicates the current mutation operator, which only uses Gaussian jitter, lacks the exploratory power to escape these configurations.
+
+This edit reinstates the more powerful, multi-strategy mutation operator from previously successful versions. The new operator combines three distinct strategies:
+1. **Gaussian Jitter**: Retained for local fine-tuning of circle positions.
+2. **Swap Mutation (10% chance)**: Swaps the centers of two randomly chosen circles. This is a critical topological operator that can fundamentally alter the packing structure, enabling the algorithm to escape deep local minima by changing which circles are adjacent.
+3. **Re-initialize Mutation (5% chance)**: Moves one randomly chosen circle to a new random position. This acts as a targeted diversity injection, rescuing a single circle that has become trapped in a highly constrained area.
+
+By reintroducing these powerful exploration mechanisms, the genetic algorithm can more effectively navigate the complex solution landscape, avoid premature convergence, and discover more efficient packing arrangements. This change directly addresses the observed stagnation and is expected to significantly improve the final sum of radii.
+
+
+<<<<<<< SEARCH
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies Gaussian noise mutation to individual circle centers.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+
+ # Apply Gaussian noise only to selected circles
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+=======
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter ---
+ # Apply small perturbations for fine-tuning the positions.
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ # Swaps the positions of two circles. This is a powerful topological operator
+ # that can significantly alter the packing structure and help escape local optima.
+ if np.random.rand() < 0.10: # 10% chance to swap two random circles
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ mutated_centers[[i, j]] = mutated_centers[[j, i]]
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ # Moves a single circle to a completely new random position. This helps to
+ # rescue circles that are "trapped" in a very suboptimal location.
+ if np.random.rand() < 0.05: # 5% chance to re-initialize one random circle
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..162d95c493bc37335f1209f62a0f481d388b04a6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/edit.diff
@@ -0,0 +1,364 @@
+--- a/original.py
++++ b/original.py
+@@ -1,222 +1,224 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+ import math
+ import random
+
+-def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+- """
+- Compute maximum radii using iterative proportional scaling.
+- This version is optimized for speed during search, with a final high-precision call.
++def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
++ """
++ Compute maximum radii using iterative proportional scaling with a convergence check.
++ This version vectorizes distance calculations for improved performance.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
++ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
++
++ # Vectorized calculation for distances between all pairs
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++
++ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+- dist_sq = np.sum((centers[i] - centers[j])**2)
+- dist = np.sqrt(dist_sq)
++ dist = dists[i, j] # Use pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+- if dist < 1e-9:
+- radii[i], radii[j] = 0.0, 0.0
++ if dist < 1e-9: # Handle co-located centers: radii must be zero
++ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
++ radii[i], radii[j] = 0.0, 0.0
++ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+- updated = True
+-
++ updated = True
++
++ # Check for convergence: if no updates were made AND the max change is tiny.
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+-class SimulatedAnnealer:
+- """
+- Performs a search for an optimal circle packing using Simulated Annealing.
+- """
+- def __init__(self, n, initial_centers, config):
++class PressureDrivenRelaxation:
++ """
++ A global optimization algorithm based on simulating a physical system
++ where circles are under 'growth pressure' and repel each other and walls.
++ """
++ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+- self.centers = initial_centers
+-
+- self.temp = config['t_start']
+- self.max_step_size = config['max_step_size']
+-
+- self.energy = self._calculate_energy(self.centers)
+- self.best_centers = self.centers.copy()
+- self.best_energy = self.energy
+-
+- self.move_weights = config['move_weights']
+- self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+-
+- def _calculate_energy(self, centers):
+- """Energy is the negative sum of radii, to be minimized."""
+- radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+- return -np.sum(radii)
+-
+- def _propose_move(self):
++ self.best_centers = None
++ self.best_score = -1.0
++
++ def _update_centers_once(self, current_centers, iteration_progress):
+ """
+- Proposes a new state by selecting a move type and applying it.
+- The step size is annealed with the temperature.
++ Calculates forces and updates centers for one step of the simulation.
++ Parameters are annealed based on iteration_progress.
+ """
+- # Choose a move type based on weights
+- move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+-
+- # Anneal step size
+- t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+- current_step_size = self.max_step_size * t_progress
+-
+- return move_func(current_step_size)
+-
+- def _move_single_circle(self, step_size):
+- """Move a single randomly chosen circle."""
+- new_centers = self.centers.copy()
+- idx = random.randint(0, self.n - 1)
+-
+- displacement = np.random.normal(0, step_size, size=2)
+- new_centers[idx] += displacement
+- new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
++ # Anneal growth pressure and learning rate using decay powers
++ current_growth_pressure = self.config['final_growth_pressure'] + \
++ (self.config['initial_growth_pressure'] - self.config['final_growth_pressure']) * \
++ (1.0 - iteration_progress)**self.config['pressure_decay_power']
++
++ current_lr = self.config['final_learning_rate'] + \
++ (self.config['initial_learning_rate'] - self.config['final_learning_rate']) * \
++ (1.0 - iteration_progress)**self.config['lr_decay_power']
++
++ wall_strength = self.config['wall_strength']
++
++ # Calculate radii based on current centers
++ # This compute_max_radii is a fast version for simulation steps
++ radii = compute_max_radii(current_centers, max_iter=self.config['radius_sim_iter'])
++ pressured_radii = radii * current_growth_pressure
++
++ # Vectorized force calculations
++ diffs = current_centers[:, np.newaxis, :] - current_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ # Ensure non-zero distances for division
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ # Calculate unit vectors for repulsion directions
++ # Use np.errstate to suppress division by zero warning for co-located centers,
++ # which are then handled by replacing NaNs/Infs with 0.
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / dists[:, :, np.newaxis]
++ unit_vectors[~np.isfinite(unit_vectors)] = 0 # Replace NaNs/Infs with 0
++
++ # Sum up circle-to-circle repulsion forces
++ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
++
++ wall_forces = np.zeros_like(current_centers)
++ # Wall repulsion forces (pushing circles away from boundaries if they overlap with 'pressured_radii')
++ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 0]) # Left wall
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (current_centers[:, 0] + pressured_radii) - 1.0) # Right wall
++ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 1]) # Bottom wall
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (current_centers[:, 1] + pressured_radii) - 1.0) # Top wall
++
++ total_forces = circle_forces + wall_forces
++
++ # Update centers based on total forces and learning rate
++ new_centers = current_centers + total_forces * current_lr
++ # Clip centers to ensure they stay within the unit square
++ new_centers = np.clip(new_centers, 0.0, 1.0)
++
+ return new_centers
+
+- def _move_cluster(self, step_size):
+- """Move a small cluster of neighboring circles."""
+- new_centers = self.centers.copy()
+- cluster_size = self.config['cluster_size']
+-
+- # Pick a seed circle
+- seed_idx = random.randint(0, self.n - 1)
+-
+- # Find its neighbors
+- dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+- neighbor_indices = np.argsort(dists)[:cluster_size]
+-
+- # Apply a common displacement
+- displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+- new_centers[neighbor_indices] += displacement
+- new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+- return new_centers
+-
+- def _swap_circles(self, step_size):
+- """Swap the positions of two randomly chosen circles."""
+- new_centers = self.centers.copy()
+- if self.n < 2: return new_centers
+-
+- idx1, idx2 = random.sample(range(self.n), 2)
+- new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+- return new_centers
+-
+- def run(self):
+- """Executes the simulated annealing search."""
+- while self.temp > self.config['t_end']:
+- for _ in range(self.config['moves_per_temp']):
+- # Propose a new configuration
+- new_centers = self._propose_move()
+-
+- # Calculate its energy
+- new_energy = self._calculate_energy(new_centers)
+-
+- delta_e = new_energy - self.energy
+-
+- # Acceptance criterion
+- if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+- self.centers = new_centers
+- self.energy = new_energy
+-
+- # Update best-ever solution
+- if self.energy < self.best_energy:
+- self.best_energy = self.energy
+- self.best_centers = self.centers.copy()
+-
+- # Cool down the temperature
+- self.temp *= self.config['cooling_rate']
+-
++ def run(self, initial_centers):
++ """Executes the pressure-driven relaxation for a single start."""
++ current_centers = initial_centers.copy()
++
++ for iteration in range(self.config['total_iterations']):
++ # Calculate progress for annealing parameters
++ iteration_progress = iteration / self.config['total_iterations']
++ current_centers = self._update_centers_once(current_centers, iteration_progress)
++
++ # Periodically evaluate the actual score (without pressure) and update the best solution found
++ if iteration % self.config['eval_frequency'] == 0 or iteration == self.config['total_iterations'] - 1:
++ # Use a more thorough radius calculation for evaluation
++ radii = compute_max_radii(current_centers, max_iter=self.config['radius_iter'])
++ score = np.sum(radii)
++ if score > self.best_score:
++ self.best_score = score
++ self.best_centers = current_centers.copy()
++
+ return self.best_centers
+
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
++ Constructs a packing of 26 circles using a multi-start Pressure-Driven Relaxation approach.
+ """
+ n = 26
+ config = {
+- 'num_starts': 16, # More starts to explore different basins
+- 't_start': 0.06, # Higher initial temperature for more exploration
+- 't_end': 1e-6, # Final temperature
+- 'cooling_rate': 0.999, # Slower cooling for more thorough search
+- 'moves_per_temp': 50, # Adjusted moves to balance longer schedule
+- 'max_step_size': 0.20, # Larger initial step size
+- 'radius_iter': 250, # Radius calculation iterations during search
+- 'move_weights': [0.70, 0.20, 0.10], # Increased probability for powerful swap move
+- 'cluster_size': 5, # Slightly larger clusters
++ 'num_starts': 15, # Number of independent optimization runs
++ 'total_iterations': 6000, # Total iterations for each run (increased for more thorough search)
++ 'eval_frequency': 100, # How often to evaluate and store best solution
++
++ 'initial_learning_rate': 0.03, # Start with higher LR for broad moves
++ 'final_learning_rate': 0.0001, # End with very low LR for fine-tuning
++ 'lr_decay_power': 2.0, # Quadratic decay for learning rate
++
++ 'initial_growth_pressure': 1.1, # Start with significant pressure to avoid local minima
++ 'final_growth_pressure': 1.00001, # End with minimal pressure for tight packing
++ 'pressure_decay_power': 3.0, # Cubic decay for growth pressure (more aggressive towards end)
++
++ 'wall_strength': 0.75, # Strength of repulsion from square walls (slightly increased)
++
++ 'radius_sim_iter': 100, # Iterations for radius calculation during simulation (fast)
++ 'radius_iter': 2000, # Iterations for final high-precision radius calculation for stored best
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+- # Multi-start loop
++ # Multi-start loop to explore different basins of attraction
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+- # Prioritize structured starts (12 of 16), which are generally more promising
+- if i < 12:
+- # Start with a perturbed 5x5 grid + 1 extra circle
++ if i < config['num_starts'] // 2:
++ # Start with a perturbed 5x5 grid + 1 extra circle for structured initial states
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+- initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+-
+- # Use a richer set of candidate positions for the 26th circle
++ # Add some initial random perturbation to the grid
++ initial_centers[:25, :] += np.random.normal(0, spacing * 0.05, size=(25, 2))
++
++ # Place the 26th circle in one of several strategic candidate positions
+ extra_pos_candidates = [
+- [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
++ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners
+ [0.5, 0.5], # Center
+- [0.2, 0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8], # Grid interstitial (spacing=0.2)
+- [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
++ [0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75], # Quarter points
++ [0.1, 0.5], [0.5, 0.1], [0.9, 0.5], [0.5, 0.9] # Edge midpoints
+ ]
+- initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
++ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)] + np.random.normal(0, 0.02, size=2)
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+- # Start with a purely random configuration
++ # For other starts, use purely random configurations to ensure broad exploration
+ initial_centers = np.random.rand(n, 2)
+
+- # Run the annealer for this start
+- solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+- result_centers = solver.run()
+-
+- # Evaluate and update the best overall result
++ # Run the PressureDrivenRelaxation for this initial configuration
++ solver = PressureDrivenRelaxation(n=n, config=config)
++ result_centers = solver.run(initial_centers)
++
++ # Evaluate the final score for this run (using a high-precision radius calculation)
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+-
++
++ # Update the best overall solution found across all starts
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+-
+- # Final high-precision radius calculation for the best solution found
+- final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+-
++
++ # Final, high-precision radius calculation for the absolute best solution found
++ final_radii = compute_max_radii(best_overall_centers, max_iter=2500, convergence_threshold=1e-9)
++
+ return best_overall_centers, final_radii
+-
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..2f454fbe24c716b8e4e8ee188e38e764e23fbe09
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/main.py
@@ -0,0 +1,224 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ This version vectorizes distance calculations for improved performance.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+
+ # Vectorized calculation for distances between all pairs
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Use pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class PressureDrivenRelaxation:
+ """
+ A global optimization algorithm based on simulating a physical system
+ where circles are under 'growth pressure' and repel each other and walls.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.best_centers = None
+ self.best_score = -1.0
+
+ def _update_centers_once(self, current_centers, iteration_progress):
+ """
+ Calculates forces and updates centers for one step of the simulation.
+ Parameters are annealed based on iteration_progress.
+ """
+ # Anneal growth pressure and learning rate using decay powers
+ current_growth_pressure = self.config['final_growth_pressure'] + \
+ (self.config['initial_growth_pressure'] - self.config['final_growth_pressure']) * \
+ (1.0 - iteration_progress)**self.config['pressure_decay_power']
+
+ current_lr = self.config['final_learning_rate'] + \
+ (self.config['initial_learning_rate'] - self.config['final_learning_rate']) * \
+ (1.0 - iteration_progress)**self.config['lr_decay_power']
+
+ wall_strength = self.config['wall_strength']
+
+ # Calculate radii based on current centers
+ # This compute_max_radii is a fast version for simulation steps
+ radii = compute_max_radii(current_centers, max_iter=self.config['radius_sim_iter'])
+ pressured_radii = radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = current_centers[:, np.newaxis, :] - current_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ # Ensure non-zero distances for division
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ # Calculate unit vectors for repulsion directions
+ # Use np.errstate to suppress division by zero warning for co-located centers,
+ # which are then handled by replacing NaNs/Infs with 0.
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / dists[:, :, np.newaxis]
+ unit_vectors[~np.isfinite(unit_vectors)] = 0 # Replace NaNs/Infs with 0
+
+ # Sum up circle-to-circle repulsion forces
+ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(current_centers)
+ # Wall repulsion forces (pushing circles away from boundaries if they overlap with 'pressured_radii')
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 0]) # Left wall
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (current_centers[:, 0] + pressured_radii) - 1.0) # Right wall
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 1]) # Bottom wall
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (current_centers[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ total_forces = circle_forces + wall_forces
+
+ # Update centers based on total forces and learning rate
+ new_centers = current_centers + total_forces * current_lr
+ # Clip centers to ensure they stay within the unit square
+ new_centers = np.clip(new_centers, 0.0, 1.0)
+
+ return new_centers
+
+ def run(self, initial_centers):
+ """Executes the pressure-driven relaxation for a single start."""
+ current_centers = initial_centers.copy()
+
+ for iteration in range(self.config['total_iterations']):
+ # Calculate progress for annealing parameters
+ iteration_progress = iteration / self.config['total_iterations']
+ current_centers = self._update_centers_once(current_centers, iteration_progress)
+
+ # Periodically evaluate the actual score (without pressure) and update the best solution found
+ if iteration % self.config['eval_frequency'] == 0 or iteration == self.config['total_iterations'] - 1:
+ # Use a more thorough radius calculation for evaluation
+ radii = compute_max_radii(current_centers, max_iter=self.config['radius_iter'])
+ score = np.sum(radii)
+ if score > self.best_score:
+ self.best_score = score
+ self.best_centers = current_centers.copy()
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Pressure-Driven Relaxation approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 15, # Number of independent optimization runs
+ 'total_iterations': 6000, # Total iterations for each run (increased for more thorough search)
+ 'eval_frequency': 100, # How often to evaluate and store best solution
+
+ 'initial_learning_rate': 0.03, # Start with higher LR for broad moves
+ 'final_learning_rate': 0.0001, # End with very low LR for fine-tuning
+ 'lr_decay_power': 2.0, # Quadratic decay for learning rate
+
+ 'initial_growth_pressure': 1.1, # Start with significant pressure to avoid local minima
+ 'final_growth_pressure': 1.00001, # End with minimal pressure for tight packing
+ 'pressure_decay_power': 3.0, # Cubic decay for growth pressure (more aggressive towards end)
+
+ 'wall_strength': 0.75, # Strength of repulsion from square walls (slightly increased)
+
+ 'radius_sim_iter': 100, # Iterations for radius calculation during simulation (fast)
+ 'radius_iter': 2000, # Iterations for final high-precision radius calculation for stored best
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop to explore different basins of attraction
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle for structured initial states
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ # Add some initial random perturbation to the grid
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.05, size=(25, 2))
+
+ # Place the 26th circle in one of several strategic candidate positions
+ extra_pos_candidates = [
+ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners
+ [0.5, 0.5], # Center
+ [0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75], # Quarter points
+ [0.1, 0.5], [0.5, 0.1], [0.9, 0.5], [0.5, 0.9] # Edge midpoints
+ ]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)] + np.random.normal(0, 0.02, size=2)
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # For other starts, use purely random configurations to ensure broad exploration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the PressureDrivenRelaxation for this initial configuration
+ solver = PressureDrivenRelaxation(n=n, config=config)
+ result_centers = solver.run(initial_centers)
+
+ # Evaluate the final score for this run (using a high-precision radius calculation)
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ # Update the best overall solution found across all starts
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final, high-precision radius calculation for the absolute best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2500, convergence_threshold=1e-9)
+
+ return best_overall_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fa5a9ebe7d113d7114155bce9bb2ac44a405dc4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/original.py
@@ -0,0 +1,222 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 16, # More starts to explore different basins
+ 't_start': 0.06, # Higher initial temperature for more exploration
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.999, # Slower cooling for more thorough search
+ 'moves_per_temp': 50, # Adjusted moves to balance longer schedule
+ 'max_step_size': 0.20, # Larger initial step size
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.70, 0.20, 0.10], # Increased probability for powerful swap move
+ 'cluster_size': 5, # Slightly larger clusters
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ # Prioritize structured starts (12 of 16), which are generally more promising
+ if i < 12:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Use a richer set of candidate positions for the 26th circle
+ extra_pos_candidates = [
+ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
+ [0.5, 0.5], # Center
+ [0.2, 0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8], # Grid interstitial (spacing=0.2)
+ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
+ ]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..03f49aee5b4ec0b48619e3a782868269ae34d53e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_122/rewrite.txt
@@ -0,0 +1,214 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ This version vectorizes distance calculations for improved performance.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+
+ # Vectorized calculation for distances between all pairs
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Use pre-calculated distance
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class PressureDrivenRelaxation:
+ """
+ A global optimization algorithm based on simulating a physical system
+ where circles are under 'growth pressure' and repel each other and walls.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.best_centers = None
+ self.best_score = -1.0
+
+ def _update_centers_once(self, current_centers, iteration_progress):
+ """
+ Calculates forces and updates centers for one step of the simulation.
+ Parameters are annealed based on iteration_progress.
+ """
+ # Anneal growth pressure and learning rate using decay powers
+ current_growth_pressure = self.config['final_growth_pressure'] + \
+ (self.config['initial_growth_pressure'] - self.config['final_growth_pressure']) * \
+ (1.0 - iteration_progress)**self.config['pressure_decay_power']
+
+ current_lr = self.config['final_learning_rate'] + \
+ (self.config['initial_learning_rate'] - self.config['final_learning_rate']) * \
+ (1.0 - iteration_progress)**self.config['lr_decay_power']
+
+ wall_strength = self.config['wall_strength']
+
+ # Calculate radii based on current centers
+ # This compute_max_radii is a fast version for simulation steps
+ radii = compute_max_radii(current_centers, max_iter=self.config['radius_sim_iter'])
+ pressured_radii = radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = current_centers[:, np.newaxis, :] - current_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ # Ensure non-zero distances for division
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ # Calculate unit vectors for repulsion directions
+ # Use np.errstate to suppress division by zero warning for co-located centers,
+ # which are then handled by replacing NaNs/Infs with 0.
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / dists[:, :, np.newaxis]
+ unit_vectors[~np.isfinite(unit_vectors)] = 0 # Replace NaNs/Infs with 0
+
+ # Sum up circle-to-circle repulsion forces
+ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(current_centers)
+ # Wall repulsion forces (pushing circles away from boundaries if they overlap with 'pressured_radii')
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 0]) # Left wall
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (current_centers[:, 0] + pressured_radii) - 1.0) # Right wall
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 1]) # Bottom wall
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (current_centers[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ total_forces = circle_forces + wall_forces
+
+ # Update centers based on total forces and learning rate
+ new_centers = current_centers + total_forces * current_lr
+ # Clip centers to ensure they stay within the unit square
+ new_centers = np.clip(new_centers, 0.0, 1.0)
+
+ return new_centers
+
+ def run(self, initial_centers):
+ """Executes the pressure-driven relaxation for a single start."""
+ current_centers = initial_centers.copy()
+
+ for iteration in range(self.config['total_iterations']):
+ # Calculate progress for annealing parameters
+ iteration_progress = iteration / self.config['total_iterations']
+ current_centers = self._update_centers_once(current_centers, iteration_progress)
+
+ # Periodically evaluate the actual score (without pressure) and update the best solution found
+ if iteration % self.config['eval_frequency'] == 0 or iteration == self.config['total_iterations'] - 1:
+ # Use a more thorough radius calculation for evaluation
+ radii = compute_max_radii(current_centers, max_iter=self.config['radius_iter'])
+ score = np.sum(radii)
+ if score > self.best_score:
+ self.best_score = score
+ self.best_centers = current_centers.copy()
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Pressure-Driven Relaxation approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 15, # Number of independent optimization runs
+ 'total_iterations': 6000, # Total iterations for each run (increased for more thorough search)
+ 'eval_frequency': 100, # How often to evaluate and store best solution
+
+ 'initial_learning_rate': 0.03, # Start with higher LR for broad moves
+ 'final_learning_rate': 0.0001, # End with very low LR for fine-tuning
+ 'lr_decay_power': 2.0, # Quadratic decay for learning rate
+
+ 'initial_growth_pressure': 1.1, # Start with significant pressure to avoid local minima
+ 'final_growth_pressure': 1.00001, # End with minimal pressure for tight packing
+ 'pressure_decay_power': 3.0, # Cubic decay for growth pressure (more aggressive towards end)
+
+ 'wall_strength': 0.75, # Strength of repulsion from square walls (slightly increased)
+
+ 'radius_sim_iter': 100, # Iterations for radius calculation during simulation (fast)
+ 'radius_iter': 2000, # Iterations for final high-precision radius calculation for stored best
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop to explore different basins of attraction
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle for structured initial states
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ # Add some initial random perturbation to the grid
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.05, size=(25, 2))
+
+ # Place the 26th circle in one of several strategic candidate positions
+ extra_pos_candidates = [
+ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners
+ [0.5, 0.5], # Center
+ [0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75], # Quarter points
+ [0.1, 0.5], [0.5, 0.1], [0.9, 0.5], [0.5, 0.9] # Edge midpoints
+ ]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)] + np.random.normal(0, 0.02, size=2)
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # For other starts, use purely random configurations to ensure broad exploration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the PressureDrivenRelaxation for this initial configuration
+ solver = PressureDrivenRelaxation(n=n, config=config)
+ result_centers = solver.run(initial_centers)
+
+ # Evaluate the final score for this run (using a high-precision radius calculation)
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ # Update the best overall solution found across all starts
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final, high-precision radius calculation for the absolute best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2500, convergence_threshold=1e-9)
+
+ return best_overall_centers, final_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5074551f1d817d2bc2d0be44f96025f983e83b70
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..f0bf614dd166ad089e27757366caf9dc97fbe933
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/edit.diff
@@ -0,0 +1,520 @@
+--- a/original.py
++++ b/original.py
+@@ -1,188 +1,352 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+-from scipy.optimize import minimize, NonlinearConstraint
+-
+-def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+- """
+- Compute maximum radii using iterative proportional scaling with a convergence check.
+- This function is used for robust initial radius estimation and final evaluation,
+- but not directly within the core NLP optimization loop.
++
++# --- Core Utility: Radius Calculation ---
++def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-8):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ This uses an iterative proportional scaling method with a convergence check.
+ """
+ n = centers.shape[0]
+- if n == 0:
++ if n == 0: # Handle empty input for robustness
+ return np.array([])
+
+- # Initialize radii to be constrained by walls
++ # Initialize radii to be limited by wall distance, ensuring they stay within the unit square
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
++
++ for _ in range(max_iter):
++ updated_in_pass = False
++ previous_radii = radii.copy()
++
++ # Iteratively shrink radii that cause overlaps between circles
++ for i in range(n):
++ for j in range(i + 1, n): # Compare each unique pair of circles
++ vec = centers[i] - centers[j]
++ dist = np.linalg.norm(vec)
++
++ if dist < 1e-9: # Handle co-located or very close centers
++ if radii[i] > 0 or radii[j] > 0:
++ radii[i], radii[j] = 0.0, 0.0 # Force radii to zero to avoid infinite overlap
++ updated_in_pass = True
++ continue
++
++ if radii[i] + radii[j] > dist: # If circles overlap
++ scale = dist / (radii[i] + radii[j]) # Calculate scaling factor to make them just touch
++ radii[i] *= scale
++ radii[j] *= scale
++ updated_in_pass = True
++
++ # Check for convergence: if no significant changes occurred in this pass
++ max_delta_r = np.max(np.abs(radii - previous_radii))
++ if not updated_in_pass and max_delta_r < convergence_epsilon:
++ break
++
++ return np.maximum(radii, 0.0) # Ensure no radii are negative due to numerical instability
++
++# --- Encapsulated Local Searcher (Force-directed simulation) ---
++class HybridLocalSearcher:
++ """
++ A force-directed local search optimizer. This class encapsulates a
++ two-phase physics simulation logic to refine circle positions.
++ """
++ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
++ wall_strength=0.8, initial_growth_pressure=1.03,
++ final_growth_pressure=1.001, fine_tune_lr=0.001,
++ radius_sim_iter=80):
++ self.sim_iter = sim_iter
++ self.fine_tune_iter = fine_tune_iter
++ self.learning_rate = learning_rate
++ self.wall_strength = wall_strength
++ self.initial_growth_pressure = initial_growth_pressure
++ self.final_growth_pressure = final_growth_pressure
++ self.fine_tune_lr = fine_tune_lr
++ self.radius_sim_iter = radius_sim_iter
++
++ def optimize(self, centers):
++ """Refines circle centers using the force-directed simulation."""
++
++ # --- Phase 1: Annealed Growth Pressure ---
++ # This phase uses artificially inflated radii to create stronger repulsive forces,
++ # helping circles to escape local minima and find more open configurations.
++ for i_iter in range(self.sim_iter):
++ progress = i_iter / self.sim_iter
++ # Growth pressure anneals from initial to final value over iterations
++ growth_pressure = self.final_growth_pressure + \
++ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
++
++ # Use faster radius calculation during simulation for performance
++ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
++ pressured_radii = radii * growth_pressure # Apply growth pressure
++
++ forces = self._calculate_forces(centers, pressured_radii)
++
++ # Learning rate also anneals, starting larger for exploration and shrinking for stability
++ lr = self.learning_rate * (1.0 - progress)**2
++ centers += forces * lr
++ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
++
++ # --- Phase 2: Fine-Tuning ---
++ # After the aggressive exploration of Phase 1, this phase settles the packing
++ # without artificial pressure, allowing for precise placement based on actual overlaps.
++ for _ in range(self.fine_tune_iter):
++ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
++ forces = self._calculate_forces(centers, radii)
++ centers += forces * self.fine_tune_lr
++ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
++
++ return centers
++
++ def _calculate_forces(self, centers, effective_radii):
++ """
++ Calculates repulsion forces from circle overlaps and wall proximity.
++ `effective_radii` can be `pressured_radii` (for Phase 1) or `actual radii` (for Phase 2).
++ """
++ forces = np.zeros_like(centers)
++
++ # Vectorized Circle-to-circle repulsion
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
++
++ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
++ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
++
++ force_magnitudes = overlaps # Force magnitude proportional to overlap
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
++ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
++
++ # Vectorized Wall repulsion (pushes circles away from boundaries)
++ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
++ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
++ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
++ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
++
++ return forces
++
++# --- GA Core Components ---
++class Individual:
++ """Represents a single candidate packing solution (a set of circle centers)."""
++ def __init__(self, centers_array, n_circles):
++ self.centers = np.array(centers_array).reshape(n_circles, 2)
++ self.radii = None
++ self.fitness = -1.0 # Sum of radii, initialized to a low value
++
++ def evaluate_fitness(self, radius_iter_val):
++ """Calculates the sum of radii for the current center configuration."""
++ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
++ self.fitness = np.sum(self.radii)
++ return self.fitness
++
++def initialize_population(n_circles, population_size):
++ """
++ Initializes a diverse population for the Genetic Algorithm.
++ Includes multiple grid-based patterns (5x5, 6x5) and purely random configurations
++ to ensure broad exploration of the search space.
++ """
++ population = []
++
++ # 1. Base from 5x5 grid + one interstitial circle
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ grid_centers_5x5_base = np.zeros((n_circles, 2))
++ k = 0
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ if k < n_circles - 1: # Fill up to N-1 circles
++ grid_centers_5x5_base[k, 0] = (i + 0.5) * spacing
++ grid_centers_5x5_base[k, 1] = (j + 0.5) * spacing
++ k += 1
++ else:
++ break
++ # Place the Nth circle at a common interstitial point
++ if k < n_circles:
++ grid_centers_5x5_base[k] = [spacing, spacing]
++
++ # Add the base 5x5 grid configuration and some perturbed versions
++ population.append(Individual(grid_centers_5x5_base.copy(), n_circles))
++ num_perturbed_5x5 = population_size // 8 # Add a good fraction of perturbed 5x5
++ perturbation_strength_grid = 0.02
++ for _ in range(num_perturbed_5x5):
++ perturbed_centers = grid_centers_5x5_base + np.random.normal(0, perturbation_strength_grid, (n_circles, 2))
++ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
++
++ # 2. Add configurations based on a 6x5 grid structure for additional topological diversity
++ if n_circles <= 30: # Only add if N is suitable for a 6x5 grid
++ num_6x5_configs = population_size // 8 # Add a few of these
++ nx, ny = 6, 5
++ spacing_x = 1.0 / nx
++ spacing_y = 1.0 / ny
++ grid_6x5_base = np.zeros((nx * ny, 2))
++ k_6x5 = 0
++ for j_6x5 in range(ny):
++ for i_6x5 in range(nx):
++ grid_6x5_base[k_6x5, 0] = (i_6x5 + 0.5) * spacing_x
++ grid_6x5_base[k_6x5, 1] = (j_6x5 + 0.5) * spacing_y
++ k_6x5 += 1
++
++ for _ in range(num_6x5_configs):
++ config_6x5 = grid_6x5_base[:n_circles].copy() # Take first N circles from the grid
++ config_6x5 += np.random.normal(0, 0.02, (n_circles, 2)) # Perturb slightly
++ population.append(Individual(np.clip(config_6x5, 0.0, 1.0), n_circles))
++
++ # 3. Fill the rest of the population with purely random configurations for broad diversity
++ while len(population) < population_size:
++ random_centers = np.random.rand(n_circles, 2)
++ population.append(Individual(random_centers, n_circles))
+
+- for _ in range(max_iter):
+- old_radii = radii.copy()
+- updated = False
+-
+- # Vectorized calculation for distances to improve performance
+- diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+-
+- # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = dists[i, j] # Pre-calculated distance
+-
+- # If circles overlap
+- if radii[i] + radii[j] > dist:
+- if dist < 1e-9: # Handle co-located centers: radii must be zero
+- if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+- radii[i], radii[j] = 0.0, 0.0
+- updated = True
+- else:
+- # Scale down radii proportionally to resolve overlap
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+- updated = True
+-
+- # Check for convergence: if no updates were made AND the max change is tiny.
+- if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+- break
+-
+- return np.maximum(radii, 0) # Ensure no negative radii (safeguard)
+-
+-
+-def objective_function(z, n_circles):
+- """
+- Objective function for SciPy minimize. We want to maximize the sum of radii,
+- so we minimize the negative sum of radii.
+- z is a 1D array: [x1..xn, y1..yn, r1..rn]
+- """
+- radii = z[2 * n_circles : 3 * n_circles]
+- return -np.sum(radii)
+-
+-def constraint_function(z, n_circles):
+- """
+- Constraint function for SciPy minimize. All constraints must be >= 0.
+- - Wall constraints: ensure circles stay within [0,1]x[0,1].
+- - Non-overlap constraints: ensure no two circles overlap.
+- """
+- x, y, r = z[:n_circles], z[n_circles : 2 * n_circles], z[2 * n_circles : 3 * n_circles]
+-
+- # Wall constraints:
+- # x_i - r_i >= 0
+- # 1 - x_i - r_i >= 0
+- # y_i - r_i >= 0
+- # 1 - y_i - r_i >= 0
+- wall_constraints = np.concatenate([
+- x - r,
+- (1 - x) - r,
+- y - r,
+- (1 - y) - r
+- ])
+-
+- # Non-overlap constraints: (x_i - x_j)^2 + (y_i - y_j)^2 >= (r_i + r_j)^2
+- # Formulated as: (x_i - x_j)^2 + (y_i - y_j)^2 - (r_i + r_j)^2 >= 0
+- pair_constraints = []
+- for i in range(n_circles):
+- for j in range(i + 1, n_circles):
+- dx = x[i] - x[j]
+- dy = y[i] - y[j]
+- dr = r[i] + r[j]
+- pair_constraints.append(dx * dx + dy * dy - dr * dr)
+-
+- return np.concatenate([wall_constraints, np.array(pair_constraints)])
+-
+-
++ return population
++
++def select_parents(population, tournament_size):
++ """Selects two distinct parents using tournament selection."""
++ # Select parent 1 by picking the fittest from a random tournament subset
++ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
++ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
++
++ # Select parent 2, ensuring it's different from parent 1
++ while True:
++ tournament_p2 = np.random.choice(population, tournament_size, replace=False)
++ parent2 = max(tournament_p2, key=lambda ind: ind.fitness)
++ if parent2 is not parent1: # Ensure parents are distinct instances
++ return parent1, parent2
++
++def crossover(parent1, parent2, n_circles):
++ """
++ Performs uniform crossover: for each circle, its position (x,y) is inherited
++ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
++ """
++ child_centers = np.zeros((n_circles, 2))
++ # A mask for each circle, applied to both x and y coordinates
++ mask = np.random.rand(n_circles, 1) < 0.5
++ child_centers = np.where(mask, parent1.centers, parent2.centers)
++ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
++
++def mutate(individual, mutation_rate, mutation_strength, n_circles):
++ """
++ Applies an enhanced mutation operator with multiple strategies:
++ 1. Standard Gaussian Jitter (for local fine-tuning).
++ 2. Swap Mutation (for large-scale topological changes).
++ 3. Re-initialize Mutation (to escape from very poor local circle placements).
++ """
++ mutated_centers = individual.centers.copy()
++
++ # --- Strategy 1: Standard Gaussian Jitter (applied to a subset of circles) ---
++ mask = np.random.rand(n_circles) < mutation_rate
++ if np.any(mask):
++ noise = np.random.normal(0, mutation_strength, (mask.sum(), 2))
++ mutated_centers[mask] += noise
++
++ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
++ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
++ if n_circles > 1:
++ i, j = np.random.choice(n_circles, 2, replace=False)
++ mutated_centers[[i, j]] = mutated_centers[[j, i]] # Efficiently swap rows
++
++ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
++ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
++ idx_to_reinit = np.random.randint(0, n_circles)
++ mutated_centers[idx_to_reinit] = np.random.rand(2) # Move to a new random location
++
++ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
++ return individual
++
++# --- Main Orchestrator: Memetic Algorithm ---
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles using a multi-start Non-Linear Optimization approach.
+- """
+- n = 26 # Number of circles
+- num_starts = 15 # Number of different initial guesses for multi-start optimization
+-
+- best_overall_centers = None
+- best_overall_sum_radii = -1.0
+-
+- # Define bounds for x, y, and r for each circle
+- # x_i in [0, 1], y_i in [0, 1], r_i in [0, 0.5]
+- bounds = [(0, 1)] * n + [(0, 1)] * n + [(0, 0.5)] * n
+-
+- # Total number of constraints
+- num_wall_constraints = 4 * n
+- num_pair_constraints = n * (n - 1) // 2
+- total_constraints = num_wall_constraints + num_pair_constraints
+-
+- # Lower bounds for all constraints (all must be >= 0)
+- constraint_lower_bounds = np.zeros(total_constraints)
+- # Upper bounds for all constraints (can be infinity)
+- constraint_upper_bounds = np.full(total_constraints, np.inf)
+-
+- # Create the NonlinearConstraint object once for efficiency
+- nlc = NonlinearConstraint(lambda z: constraint_function(z, n), constraint_lower_bounds, constraint_upper_bounds)
+-
+- for start_idx in range(num_starts):
+- # --- Generate Initial Guess for Optimization ---
+- # Start with random center positions
+- initial_centers_xy = np.random.rand(n, 2)
+-
+- # Calculate robust initial radii based on these centers.
+- # This provides a feasible starting point where circles don't overlap
+- # and are within bounds (for the initial centers).
+- initial_radii_guess = compute_max_radii(initial_centers_xy, max_iter=100, convergence_threshold=1e-5)
+-
+- # Combine into the single 1D optimization variable vector z: [x_coords, y_coords, radii]
+- x0 = np.concatenate([initial_centers_xy[:, 0], initial_centers_xy[:, 1], initial_radii_guess])
+-
+- # --- Run the Non-Linear Optimization ---
+- # SLSQP (Sequential Least Squares Programming) is chosen for its efficiency
+- # with non-linear objectives and constraints.
+- result = minimize(
+- fun=objective_function, # Function to minimize (negative sum of radii)
+- x0=x0, # Initial guess
+- args=(n,), # Additional arguments for objective_function
+- method='SLSQP', # Optimization method
+- bounds=bounds, # Bounds for each variable (x, y, r)
+- constraints=[nlc], # Nonlinear constraints
+- options={'ftol': 1e-7, 'maxiter': 750, 'disp': False} # Optimization parameters
+- )
+-
+- # --- Evaluate and Update Best Solution ---
+- if result.success:
+- # Extract optimized centers and radii from the result
+- optimized_z = result.x
+- optimized_centers = np.column_stack([optimized_z[:n], optimized_z[n : 2 * n]])
+-
+- # Re-compute radii using the robust compute_max_radii function.
+- # This step is crucial because the NLP solver might leave small
+- # numerical inaccuracies or slack in constraints. This ensures
+- # the final radii are truly maximal and non-overlapping.
+- final_radii_for_eval = compute_max_radii(optimized_centers, max_iter=2500, convergence_threshold=1e-10)
+- current_sum_radii = np.sum(final_radii_for_eval)
+-
+- if current_sum_radii > best_overall_sum_radii:
+- best_overall_sum_radii = current_sum_radii
+- best_overall_centers = optimized_centers
+- # else:
+- # print(f"Optimization for start {start_idx} failed: {result.message}")
+- # print(f"Current best sum: {best_overall_sum_radii}")
+-
+-
+- # Fallback in case no optimization was successful (should be rare)
+- if best_overall_centers is None:
+- best_overall_centers = np.random.rand(n, 2)
+-
+- # Final, high-precision radius calculation for the best centers found
+- # This is the definitive calculation for the problem metric.
+- final_radii = compute_max_radii(best_overall_centers, max_iter=2500, convergence_threshold=1e-10)
+-
+- return best_overall_centers, final_radii
++ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
++ a Genetic Algorithm with a powerful force-directed local search.
++ This approach combines the exploration power of GA with the refinement capability
++ of local search, incorporating best practices from high-scoring prior programs.
++ """
++ n_circles = 26
++
++ # --- Hyperparameters for Memetic Algorithm ---
++ POPULATION_SIZE = 120 # Number of candidate solutions in each generation
++ NUM_GENERATIONS = 800 # Total number of evolutionary steps
++ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
++ INITIAL_MUTATION_STRENGTH = 0.08 # Initial standard deviation for Gaussian mutation noise
++ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
++ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
++ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
++ LOCAL_SEARCH_PROB = 0.20 # Probability to apply the powerful local search to a new child
++
++ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
++ FINAL_RADIUS_ITER = 500 # Iterations for final high-precision evaluation of the best solution
++
++ # --- Initialization ---
++ population = initialize_population(n_circles, POPULATION_SIZE)
++ local_searcher = HybridLocalSearcher() # Instantiate local searcher with default params for main loop
++
++ # --- Crucial Memetic Step: Initial Local Optimization of Entire Population ---
++ # Applying local search to all individuals in the initial population provides
++ # a strong, diverse set of locally optimized starting points for the GA.
++ # This significantly improves the quality of solutions found early on.
++ for individual in population:
++ individual.centers = local_searcher.optimize(individual.centers.copy())
++ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
++
++ best_individual_overall = max(population, key=lambda ind: ind.fitness)
++
++ # --- Main Genetic Algorithm Loop ---
++ for generation in range(NUM_GENERATIONS):
++ # Anneal mutation strength: decreases over time for fine-tuning
++ progress = generation / NUM_GENERATIONS
++ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
++ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
++
++ new_population = []
++
++ # Elitism: Directly carry over the best individuals to the next generation
++ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
++ new_population.extend(sorted_population[:ELITISM_COUNT])
++
++ # Generate the rest of the new population through selection, crossover, and mutation
++ while len(new_population) < POPULATION_SIZE:
++ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
++ child = crossover(parent1, parent2, n_circles)
++
++ # --- Hybrid Operator: Probabilistic Local Search ---
++ # Apply the powerful local search to a subset of children. This is a key
++ # memetic step, allowing the GA to guide global search while local search
++ # refines promising solutions efficiently.
++ if np.random.rand() < LOCAL_SEARCH_PROB:
++ child.centers = local_searcher.optimize(child.centers.copy())
++
++ # Always apply mutation for ongoing diversity and exploration
++ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
++
++ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
++ new_population.append(child)
++
++ population = new_population # Replace old population with the new one
++
++ # Update the overall best individual found across all generations
++ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
++ if current_best_in_gen.fitness > best_individual_overall.fitness:
++ # Create a true copy to prevent the best solution from being modified by reference
++ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
++ best_individual_overall.fitness = current_best_in_gen.fitness
++ best_individual_overall.radii = current_best_in_gen.radii # Also copy radii for consistency
++
++ # Final polish on the best solution found with a more intensive local search
++ # These parameters are chosen to be more aggressive and thorough for the final step.
++ final_searcher = HybridLocalSearcher(sim_iter=600, fine_tune_iter=250, radius_sim_iter=200,
++ initial_growth_pressure=1.05, learning_rate=0.03)
++ best_individual_overall.centers = final_searcher.optimize(best_individual_overall.centers.copy())
++ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
++
++ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..2796e353daae61e1f4bf1c90ed5e54c95c2a7e65
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/main.py
@@ -0,0 +1,352 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with a convergence check.
+ """
+ n = centers.shape[0]
+ if n == 0: # Handle empty input for robustness
+ return np.array([])
+
+ # Initialize radii to be limited by wall distance, ensuring they stay within the unit square
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps between circles
+ for i in range(n):
+ for j in range(i + 1, n): # Compare each unique pair of circles
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Handle co-located or very close centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0 # Force radii to zero to avoid infinite overlap
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist: # If circles overlap
+ scale = dist / (radii[i] + radii[j]) # Calculate scaling factor to make them just touch
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence: if no significant changes occurred in this pass
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no radii are negative due to numerical instability
+
+# --- Encapsulated Local Searcher (Force-directed simulation) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer. This class encapsulates a
+ two-phase physics simulation logic to refine circle positions.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.03,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create stronger repulsive forces,
+ # helping circles to escape local minima and find more open configurations.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value over iterations
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure # Apply growth pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration of Phase 1, this phase settles the packing
+ # without artificial pressure, allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for Phase 1) or `actual radii` (for Phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes multiple grid-based patterns (5x5, 6x5) and purely random configurations
+ to ensure broad exploration of the search space.
+ """
+ population = []
+
+ # 1. Base from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_5x5_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill up to N-1 circles
+ grid_centers_5x5_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_5x5_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the Nth circle at a common interstitial point
+ if k < n_circles:
+ grid_centers_5x5_base[k] = [spacing, spacing]
+
+ # Add the base 5x5 grid configuration and some perturbed versions
+ population.append(Individual(grid_centers_5x5_base.copy(), n_circles))
+ num_perturbed_5x5 = population_size // 8 # Add a good fraction of perturbed 5x5
+ perturbation_strength_grid = 0.02
+ for _ in range(num_perturbed_5x5):
+ perturbed_centers = grid_centers_5x5_base + np.random.normal(0, perturbation_strength_grid, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Add configurations based on a 6x5 grid structure for additional topological diversity
+ if n_circles <= 30: # Only add if N is suitable for a 6x5 grid
+ num_6x5_configs = population_size // 8 # Add a few of these
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5_base = np.zeros((nx * ny, 2))
+ k_6x5 = 0
+ for j_6x5 in range(ny):
+ for i_6x5 in range(nx):
+ grid_6x5_base[k_6x5, 0] = (i_6x5 + 0.5) * spacing_x
+ grid_6x5_base[k_6x5, 1] = (j_6x5 + 0.5) * spacing_y
+ k_6x5 += 1
+
+ for _ in range(num_6x5_configs):
+ config_6x5 = grid_6x5_base[:n_circles].copy() # Take first N circles from the grid
+ config_6x5 += np.random.normal(0, 0.02, (n_circles, 2)) # Perturb slightly
+ population.append(Individual(np.clip(config_6x5, 0.0, 1.0), n_circles))
+
+ # 3. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ while True:
+ tournament_p2 = np.random.choice(population, tournament_size, replace=False)
+ parent2 = max(tournament_p2, key=lambda ind: ind.fitness)
+ if parent2 is not parent1: # Ensure parents are distinct instances
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ # A mask for each circle, applied to both x and y coordinates
+ mask = np.random.rand(n_circles, 1) < 0.5
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter (applied to a subset of circles) ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ noise = np.random.normal(0, mutation_strength, (mask.sum(), 2))
+ mutated_centers[mask] += noise
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ mutated_centers[[i, j]] = mutated_centers[[j, i]] # Efficiently swap rows
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2) # Move to a new random location
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 120 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 800 # Total number of evolutionary steps
+ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.08 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.20 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 500 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate local searcher with default params for main loop
+
+ # --- Crucial Memetic Step: Initial Local Optimization of Entire Population ---
+ # Applying local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for individual in population:
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a true copy to prevent the best solution from being modified by reference
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+ best_individual_overall.radii = current_best_in_gen.radii # Also copy radii for consistency
+
+ # Final polish on the best solution found with a more intensive local search
+ # These parameters are chosen to be more aggressive and thorough for the final step.
+ final_searcher = HybridLocalSearcher(sim_iter=600, fine_tune_iter=250, radius_sim_iter=200,
+ initial_growth_pressure=1.05, learning_rate=0.03)
+ best_individual_overall.centers = final_searcher.optimize(best_individual_overall.centers.copy())
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..6133abf4fa4c74f373ba6b064ffdb333db5f4ad8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/original.py
@@ -0,0 +1,188 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+from scipy.optimize import minimize, NonlinearConstraint
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ This function is used for robust initial radius estimation and final evaluation,
+ but not directly within the core NLP optimization loop.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ # Initialize radii to be constrained by walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+
+ # Vectorized calculation for distances to improve performance
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = dists[i, j] # Pre-calculated distance
+
+ # If circles overlap
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ # Scale down radii proportionally to resolve overlap
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii (safeguard)
+
+
+def objective_function(z, n_circles):
+ """
+ Objective function for SciPy minimize. We want to maximize the sum of radii,
+ so we minimize the negative sum of radii.
+ z is a 1D array: [x1..xn, y1..yn, r1..rn]
+ """
+ radii = z[2 * n_circles : 3 * n_circles]
+ return -np.sum(radii)
+
+def constraint_function(z, n_circles):
+ """
+ Constraint function for SciPy minimize. All constraints must be >= 0.
+ - Wall constraints: ensure circles stay within [0,1]x[0,1].
+ - Non-overlap constraints: ensure no two circles overlap.
+ """
+ x, y, r = z[:n_circles], z[n_circles : 2 * n_circles], z[2 * n_circles : 3 * n_circles]
+
+ # Wall constraints:
+ # x_i - r_i >= 0
+ # 1 - x_i - r_i >= 0
+ # y_i - r_i >= 0
+ # 1 - y_i - r_i >= 0
+ wall_constraints = np.concatenate([
+ x - r,
+ (1 - x) - r,
+ y - r,
+ (1 - y) - r
+ ])
+
+ # Non-overlap constraints: (x_i - x_j)^2 + (y_i - y_j)^2 >= (r_i + r_j)^2
+ # Formulated as: (x_i - x_j)^2 + (y_i - y_j)^2 - (r_i + r_j)^2 >= 0
+ pair_constraints = []
+ for i in range(n_circles):
+ for j in range(i + 1, n_circles):
+ dx = x[i] - x[j]
+ dy = y[i] - y[j]
+ dr = r[i] + r[j]
+ pair_constraints.append(dx * dx + dy * dy - dr * dr)
+
+ return np.concatenate([wall_constraints, np.array(pair_constraints)])
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Non-Linear Optimization approach.
+ """
+ n = 26 # Number of circles
+ num_starts = 15 # Number of different initial guesses for multi-start optimization
+
+ best_overall_centers = None
+ best_overall_sum_radii = -1.0
+
+ # Define bounds for x, y, and r for each circle
+ # x_i in [0, 1], y_i in [0, 1], r_i in [0, 0.5]
+ bounds = [(0, 1)] * n + [(0, 1)] * n + [(0, 0.5)] * n
+
+ # Total number of constraints
+ num_wall_constraints = 4 * n
+ num_pair_constraints = n * (n - 1) // 2
+ total_constraints = num_wall_constraints + num_pair_constraints
+
+ # Lower bounds for all constraints (all must be >= 0)
+ constraint_lower_bounds = np.zeros(total_constraints)
+ # Upper bounds for all constraints (can be infinity)
+ constraint_upper_bounds = np.full(total_constraints, np.inf)
+
+ # Create the NonlinearConstraint object once for efficiency
+ nlc = NonlinearConstraint(lambda z: constraint_function(z, n), constraint_lower_bounds, constraint_upper_bounds)
+
+ for start_idx in range(num_starts):
+ # --- Generate Initial Guess for Optimization ---
+ # Start with random center positions
+ initial_centers_xy = np.random.rand(n, 2)
+
+ # Calculate robust initial radii based on these centers.
+ # This provides a feasible starting point where circles don't overlap
+ # and are within bounds (for the initial centers).
+ initial_radii_guess = compute_max_radii(initial_centers_xy, max_iter=100, convergence_threshold=1e-5)
+
+ # Combine into the single 1D optimization variable vector z: [x_coords, y_coords, radii]
+ x0 = np.concatenate([initial_centers_xy[:, 0], initial_centers_xy[:, 1], initial_radii_guess])
+
+ # --- Run the Non-Linear Optimization ---
+ # SLSQP (Sequential Least Squares Programming) is chosen for its efficiency
+ # with non-linear objectives and constraints.
+ result = minimize(
+ fun=objective_function, # Function to minimize (negative sum of radii)
+ x0=x0, # Initial guess
+ args=(n,), # Additional arguments for objective_function
+ method='SLSQP', # Optimization method
+ bounds=bounds, # Bounds for each variable (x, y, r)
+ constraints=[nlc], # Nonlinear constraints
+ options={'ftol': 1e-7, 'maxiter': 750, 'disp': False} # Optimization parameters
+ )
+
+ # --- Evaluate and Update Best Solution ---
+ if result.success:
+ # Extract optimized centers and radii from the result
+ optimized_z = result.x
+ optimized_centers = np.column_stack([optimized_z[:n], optimized_z[n : 2 * n]])
+
+ # Re-compute radii using the robust compute_max_radii function.
+ # This step is crucial because the NLP solver might leave small
+ # numerical inaccuracies or slack in constraints. This ensures
+ # the final radii are truly maximal and non-overlapping.
+ final_radii_for_eval = compute_max_radii(optimized_centers, max_iter=2500, convergence_threshold=1e-10)
+ current_sum_radii = np.sum(final_radii_for_eval)
+
+ if current_sum_radii > best_overall_sum_radii:
+ best_overall_sum_radii = current_sum_radii
+ best_overall_centers = optimized_centers
+ # else:
+ # print(f"Optimization for start {start_idx} failed: {result.message}")
+ # print(f"Current best sum: {best_overall_sum_radii}")
+
+
+ # Fallback in case no optimization was successful (should be rare)
+ if best_overall_centers is None:
+ best_overall_centers = np.random.rand(n, 2)
+
+ # Final, high-precision radius calculation for the best centers found
+ # This is the definitive calculation for the problem metric.
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2500, convergence_threshold=1e-10)
+
+ return best_overall_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..10ac361425daf3d6debb432eba7f547026a83db7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/results/job_log.err
@@ -0,0 +1,13 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
+Traceback (most recent call last):
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/evaluate.py", line 302, in
+ main(parsed_args.program_path, parsed_args.results_dir)
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/evaluate.py", line 262, in main
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..1fbfc4f397368332b0bd48fb35e3cfd631bce247
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/results/job_log.out
@@ -0,0 +1,2 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/results
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2bf8e41b679e4bfc5b6c52773c5a6aa04846c2f8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_123/rewrite.txt
@@ -0,0 +1,343 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with a convergence check.
+ """
+ n = centers.shape[0]
+ if n == 0: # Handle empty input for robustness
+ return np.array([])
+
+ # Initialize radii to be limited by wall distance, ensuring they stay within the unit square
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps between circles
+ for i in range(n):
+ for j in range(i + 1, n): # Compare each unique pair of circles
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Handle co-located or very close centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0 # Force radii to zero to avoid infinite overlap
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist: # If circles overlap
+ scale = dist / (radii[i] + radii[j]) # Calculate scaling factor to make them just touch
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence: if no significant changes occurred in this pass
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no radii are negative due to numerical instability
+
+# --- Encapsulated Local Searcher (Force-directed simulation) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer. This class encapsulates a
+ two-phase physics simulation logic to refine circle positions.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.03,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create stronger repulsive forces,
+ # helping circles to escape local minima and find more open configurations.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value over iterations
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure # Apply growth pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration of Phase 1, this phase settles the packing
+ # without artificial pressure, allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for Phase 1) or `actual radii` (for Phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes multiple grid-based patterns (5x5, 6x5) and purely random configurations
+ to ensure broad exploration of the search space.
+ """
+ population = []
+
+ # 1. Base from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_5x5_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill up to N-1 circles
+ grid_centers_5x5_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_5x5_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the Nth circle at a common interstitial point
+ if k < n_circles:
+ grid_centers_5x5_base[k] = [spacing, spacing]
+
+ # Add the base 5x5 grid configuration and some perturbed versions
+ population.append(Individual(grid_centers_5x5_base.copy(), n_circles))
+ num_perturbed_5x5 = population_size // 8 # Add a good fraction of perturbed 5x5
+ perturbation_strength_grid = 0.02
+ for _ in range(num_perturbed_5x5):
+ perturbed_centers = grid_centers_5x5_base + np.random.normal(0, perturbation_strength_grid, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Add configurations based on a 6x5 grid structure for additional topological diversity
+ if n_circles <= 30: # Only add if N is suitable for a 6x5 grid
+ num_6x5_configs = population_size // 8 # Add a few of these
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5_base = np.zeros((nx * ny, 2))
+ k_6x5 = 0
+ for j_6x5 in range(ny):
+ for i_6x5 in range(nx):
+ grid_6x5_base[k_6x5, 0] = (i_6x5 + 0.5) * spacing_x
+ grid_6x5_base[k_6x5, 1] = (j_6x5 + 0.5) * spacing_y
+ k_6x5 += 1
+
+ for _ in range(num_6x5_configs):
+ config_6x5 = grid_6x5_base[:n_circles].copy() # Take first N circles from the grid
+ config_6x5 += np.random.normal(0, 0.02, (n_circles, 2)) # Perturb slightly
+ population.append(Individual(np.clip(config_6x5, 0.0, 1.0), n_circles))
+
+ # 3. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ while True:
+ tournament_p2 = np.random.choice(population, tournament_size, replace=False)
+ parent2 = max(tournament_p2, key=lambda ind: ind.fitness)
+ if parent2 is not parent1: # Ensure parents are distinct instances
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ # A mask for each circle, applied to both x and y coordinates
+ mask = np.random.rand(n_circles, 1) < 0.5
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter (applied to a subset of circles) ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ noise = np.random.normal(0, mutation_strength, (mask.sum(), 2))
+ mutated_centers[mask] += noise
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ mutated_centers[[i, j]] = mutated_centers[[j, i]] # Efficiently swap rows
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2) # Move to a new random location
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 120 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 800 # Total number of evolutionary steps
+ MUTATION_RATE = 0.20 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.08 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.20 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 500 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate local searcher with default params for main loop
+
+ # --- Crucial Memetic Step: Initial Local Optimization of Entire Population ---
+ # Applying local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for individual in population:
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Create a true copy to prevent the best solution from being modified by reference
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+ best_individual_overall.radii = current_best_in_gen.radii # Also copy radii for consistency
+
+ # Final polish on the best solution found with a more intensive local search
+ # These parameters are chosen to be more aggressive and thorough for the final step.
+ final_searcher = HybridLocalSearcher(sim_iter=600, fine_tune_iter=250, radius_sim_iter=200,
+ initial_growth_pressure=1.05, learning_rate=0.03)
+ best_individual_overall.centers = final_searcher.optimize(best_individual_overall.centers.copy())
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ffeda55ded084504fea6df1c960c5f2ccdc6cb69
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..1863663a1e395ff4460f738300c14e1dfcc4107c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/edit.diff
@@ -0,0 +1,376 @@
+--- a/original.py
++++ b/original.py
+@@ -1,332 +1,350 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ # --- Core Utility: Radius Calculation ---
+ # Adopted from constructor; more robust with convergence check.
+ def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+ # --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+ class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+ # --- GA Core Components ---
+ class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+ def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+- Includes grid-based patterns (strong baselines) and purely random configurations
+- to ensure broad exploration.
++ Includes a wide variety of structured grid-based patterns (strong baselines) and
++ purely random configurations to ensure broad exploration.
+ """
+ population = []
+
+- # 1. Strong baseline from 5x5 grid + one interstitial circle
++ # --- Strategy: Grid + Extra Point Variations ---
++ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+- grid_centers_base = np.zeros((n_circles, 2))
++ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+- if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+- grid_centers_base[k, 0] = (i + 0.5) * spacing
+- grid_centers_base[k, 1] = (j + 0.5) * spacing
+- k += 1
+- else:
+- break
+- # Place the 26th circle at a common interstitial point
+- if k < n_circles: # For n=26, this places the 26th circle at index 25
+- grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+-
+- # Add the base grid configuration
+- population.append(Individual(grid_centers_base.copy(), n_circles))
+-
+- # Add several perturbed versions of the grid for more diverse starting points
+- for _ in range(population_size // 4): # Approximately 25% of the population
+- perturbed_centers = grid_centers_base + np.random.normal(0, 0.015, (n_circles, 2)) # Increased perturbation strength
+- population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+-
+- # 2. Fill the rest of the population with purely random configurations for broad diversity
++ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
++ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ # Strategic candidate positions for the 26th circle to seed different topologies
++ extra_pos_candidates = [
++ [spacing, spacing], # (0.2, 0.2)
++ [spacing, 0.5], # (0.2, 0.5)
++ [0.5, spacing], # (0.5, 0.2)
++ [0.5, 0.5], # Center
++ [0.05, 0.05], [0.95, 0.05], # Near corners
++ [0.05, 0.95], [0.95, 0.95],
++ [0.5, 0.1], [0.1, 0.5], # Near edge midpoints
++ [0.5, 0.9], [0.9, 0.5],
++ [0.25, 0.25], [0.75, 0.75], # Diagonal
++ [0.25, 0.75], [0.75, 0.25],
++ ]
++
++ # Generate initial individuals from these grid configurations, making up ~half the population
++ num_grid_starts = population_size // 2
++
++ for i in range(num_grid_starts):
++ current_centers = np.zeros((n_circles, 2))
++ current_centers[:n_circles-1] = base_grid_centers_25.copy()
++
++ # Cycle through the candidate positions for the extra circle
++ extra_pos = extra_pos_candidates[i % len(extra_pos_candidates)]
++ current_centers[n_circles-1] = np.array(extra_pos)
++
++ # Apply a small perturbation to add variety
++ perturbation_strength = 0.018
++ current_centers += np.random.normal(0, perturbation_strength, (n_circles, 2))
++
++ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
++
++ # --- Strategy: Completely Random Starts for maximum diversity ---
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+ def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+ def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+ def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter (always applied) ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ # Swap the centers
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+ # --- Main Orchestrator: Memetic Algorithm ---
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 100 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 750 # Total number of evolutionary steps
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a3b0820615d4d7229442755f5b6823c814c4433
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/main.py
@@ -0,0 +1,350 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes a wide variety of structured grid-based patterns (strong baselines) and
+ purely random configurations to ensure broad exploration.
+ """
+ population = []
+
+ # --- Strategy: Grid + Extra Point Variations ---
+ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
+ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate positions for the 26th circle to seed different topologies
+ extra_pos_candidates = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # Center
+ [0.05, 0.05], [0.95, 0.05], # Near corners
+ [0.05, 0.95], [0.95, 0.95],
+ [0.5, 0.1], [0.1, 0.5], # Near edge midpoints
+ [0.5, 0.9], [0.9, 0.5],
+ [0.25, 0.25], [0.75, 0.75], # Diagonal
+ [0.25, 0.75], [0.75, 0.25],
+ ]
+
+ # Generate initial individuals from these grid configurations, making up ~half the population
+ num_grid_starts = population_size // 2
+
+ for i in range(num_grid_starts):
+ current_centers = np.zeros((n_circles, 2))
+ current_centers[:n_circles-1] = base_grid_centers_25.copy()
+
+ # Cycle through the candidate positions for the extra circle
+ extra_pos = extra_pos_candidates[i % len(extra_pos_candidates)]
+ current_centers[n_circles-1] = np.array(extra_pos)
+
+ # Apply a small perturbation to add variety
+ perturbation_strength = 0.018
+ current_centers += np.random.normal(0, perturbation_strength, (n_circles, 2))
+
+ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
+
+ # --- Strategy: Completely Random Starts for maximum diversity ---
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter (always applied) ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ # Swap the centers
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 100 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 750 # Total number of evolutionary steps
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..856f3ad15a0ff6cd03c98767a690e673bc6792e1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/original.py
@@ -0,0 +1,332 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles: # For n=26, this places the 26th circle at index 25
+ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+
+ # Add the base grid configuration
+ population.append(Individual(grid_centers_base.copy(), n_circles))
+
+ # Add several perturbed versions of the grid for more diverse starting points
+ for _ in range(population_size // 4): # Approximately 25% of the population
+ perturbed_centers = grid_centers_base + np.random.normal(0, 0.015, (n_circles, 2)) # Increased perturbation strength
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies an enhanced mutation operator with multiple strategies:
+ 1. Standard Gaussian Jitter (for local fine-tuning).
+ 2. Swap Mutation (for large-scale topological changes).
+ 3. Re-initialize Mutation (to escape from very poor local circle placements).
+ """
+ mutated_centers = individual.centers.copy()
+
+ # --- Strategy 1: Standard Gaussian Jitter (always applied) ---
+ mask = np.random.rand(n_circles) < mutation_rate
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ # --- Strategy 2: Swap Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.10: # Apply swap mutation 10% of the time
+ if n_circles > 1:
+ i, j = np.random.choice(n_circles, 2, replace=False)
+ # Swap the centers
+ mutated_centers[i], mutated_centers[j] = mutated_centers[j].copy(), mutated_centers[i].copy()
+
+ # --- Strategy 3: Re-initialize Mutation (applied probabilistically) ---
+ if np.random.rand() < 0.05: # Apply re-initialization 5% of the time
+ # Pick one random circle and move it to a new random location.
+ idx_to_reinit = np.random.randint(0, n_circles)
+ mutated_centers[idx_to_reinit] = np.random.rand(2)
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 100 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 750 # Total number of evolutionary steps
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..dca1a0315df11553f46987515b1ec6b2c2208031
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/results/job_log.err
@@ -0,0 +1,25 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
+Traceback (most recent call last):
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/evaluate.py", line 302, in
+ main(parsed_args.program_path, parsed_args.results_dir)
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/evaluate.py", line 262, in main
+ metrics, correct, error_msg = run_shinka_eval(
+ ^^^^^^^^^^^^^^^^
+ File "/home/tengxiao/pj/ShinkaEvolve/shinka/core/wrap_eval.py", line 119, in run_shinka_eval
+ run_result = experiment_fn(**kwargs)
+ ^^^^^^^^^^^^^^^^^^^^^^^
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/main.py", line 347, in run_packing
+ centers, radii = construct_packing()
+ ^^^^^^^^^^^^^^^^^^^
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/main.py", line 318, in construct_packing
+ child.centers = local_searcher.optimize(child.centers.copy())
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/main.py", line 80, in optimize
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..f2b1692854258f6cb7fbeb38ad943343b086e408
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/results/job_log.out
@@ -0,0 +1,2 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/results
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7b537c9028507a478254d080be0f7756bdd2d3df
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_124/search_replace.txt
@@ -0,0 +1,209 @@
+Based on my analysis of the previous program versions and the current implementation, I've identified several key areas for improvement. The current program regressed in performance compared to prior successful versions, primarily because it lacks a final, intensive optimization step and uses less aggressive, less diverse search parameters. My proposed changes aim to reintroduce these powerful features, fix a critical bug in how the best solution is tracked, and enhance the diversity of the initial population.
+
+The core idea is to adopt a two-tier local search strategy: a faster, lighter search during the main evolutionary loop to guide the population, and a much more intensive, high-precision search applied only once to the best-found solution at the very end. This "final polish" is crucial for squeezing out the last fractions of performance. I will also increase the population size, number of generations, and local search probability to give the algorithm more opportunity to explore the solution space, and fix a bug that prevented the best solution from being properly saved.
+
+Finally, I will enhance the initial population generation to seed the search with a wider variety of structured, high-potential starting configurations, which has been a hallmark of successful prior attempts.
+
+Here are my proposed edits:
+
+
+enhance_ma_initialization
+
+
+
+This edit improves the `initialize_population` function to create a more diverse and high-quality starting population for the memetic algorithm. Instead of using a single grid-based starting point with perturbations, this version introduces a list of 16 strategically chosen candidate positions for the 26th circle. These positions include corners, edge-midpoints, and other interstitial locations. The algorithm will now generate roughly half of the initial population by cycling through these candidate positions, combined with the base 5x5 grid and small perturbations. This significantly increases the geometric and topological diversity of the initial gene pool, providing the algorithm with a much broader set of strong starting points to explore and improving the chances of discovering a superior final packing.
+
+
+
+<<<<<<< SEARCH
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles: # For n=26, this places the 26th circle at index 25
+ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+
+ # Add the base grid configuration
+ population.append(Individual(grid_centers_base.copy(), n_circles))
+
+ # Add several perturbed versions of the grid for more diverse starting points
+ for _ in range(population_size // 4): # Approximately 25% of the population
+ perturbed_centers = grid_centers_base + np.random.normal(0, 0.015, (n_circles, 2)) # Increased perturbation strength
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+=======
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes a wide variety of structured grid-based patterns (strong baselines) and
+ purely random configurations to ensure broad exploration.
+ """
+ population = []
+
+ # --- Strategy: Grid + Extra Point Variations ---
+ # Base 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers_25 = np.zeros((n_circles - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers_25[k, 0] = (i + 0.5) * spacing
+ base_grid_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate positions for the 26th circle to seed different topologies
+ extra_pos_candidates = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # Center
+ [0.05, 0.05], [0.95, 0.05], # Near corners
+ [0.05, 0.95], [0.95, 0.95],
+ [0.5, 0.1], [0.1, 0.5], # Near edge midpoints
+ [0.5, 0.9], [0.9, 0.5],
+ [0.25, 0.25], [0.75, 0.75], # Diagonal
+ [0.25, 0.75], [0.75, 0.25],
+ ]
+
+ # Generate initial individuals from these grid configurations, making up ~half the population
+ num_grid_starts = population_size // 2
+
+ for i in range(num_grid_starts):
+ current_centers = np.zeros((n_circles, 2))
+ current_centers[:n_circles-1] = base_grid_centers_25.copy()
+
+ # Cycle through the candidate positions for the extra circle
+ extra_pos = extra_pos_candidates[i % len(extra_pos_candidates)]
+ current_centers[n_circles-1] = np.array(extra_pos)
+
+ # Apply a small perturbation to add variety
+ perturbation_strength = 0.018
+ current_centers += np.random.normal(0, perturbation_strength, (n_circles, 2))
+
+ population.append(Individual(np.clip(current_centers, 0.0, 1.0), n_circles))
+
+ # --- Strategy: Completely Random Starts for maximum diversity ---
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+>>>>>>> REPLACE
+
+
+overhaul_ma_strategy
+
+
+
+This is a major overhaul of the main `construct_packing` function to re-implement several high-impact strategies from previous top-performing versions.
+
+1. **Aggressive Hyperparameters:** Increases `POPULATION_SIZE` (100->120), `NUM_GENERATIONS` (750->800), and `LOCAL_SEARCH_PROB` (0.15->0.25) to promote more thorough exploration. `GA_RADIUS_ITER` is slightly reduced (100->80) to speed up the main loop, compensating for the increased workload.
+
+2. **Two-Tier Local Search:** Introduces two distinct `HybridLocalSearcher` instances:
+ * `ga_local_searcher`: A lighter-weight version (`sim_iter=200`) used for the initial population refinement and within the main GA loop.
+ * `final_local_searcher`: A much more intensive version (`sim_iter=800`) reserved for a final polish.
+
+3. **Final Polish Step:** Reinstates the critical final optimization step. After the GA loop completes, the `final_local_searcher` is run on the best-found individual to perform a deep, high-precision refinement.
+
+4. **Bug Fix:** Fixes a critical bug where `best_individual_overall` was being updated by reference. The new code creates a deep copy of the best individual (`Individual(centers.copy(), ...)`) ensuring the best-so-far solution is not accidentally modified by later genetic operations.
+
+These changes collectively transform the algorithm into a more robust and powerful memetic search, directly addressing the regressions from prior versions and significantly increasing its potential to find better packings.
+
+
+
+<<<<<<< SEARCH
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 100 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 750 # Total number of evolutionary steps
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..8365d158e09e436126bff4f6ee13193a421c661f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/edit.diff
@@ -0,0 +1,138 @@
+--- a/original.py
++++ b/original.py
+@@ -1,111 +1,104 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+- Construct a specific arrangement of 26 circles in a unit square
+- that attempts to maximize the sum of their radii.
++ Constructs a specific arrangement of 26 circles in a unit square
++ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+- # Arrange 24 circles in a 6x4 grid
+- num_x_cells = 6
+- num_y_cells = 4
+-
+- spacing_x = 1.0 / num_x_cells
+- spacing_y = 1.0 / num_y_cells
++ # Arrange 25 circles in a dense and symmetric 5x5 grid.
++ # This is a much more stable and efficient base than a 6x4 grid.
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
+
+ k = 0
+- for j in range(num_y_cells):
+- for i in range(num_x_cells):
+- centers[k, 0] = (i + 0.5) * spacing_x
+- centers[k, 1] = (j + 0.5) * spacing_y
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ centers[k, 0] = (i + 0.5) * spacing
++ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+- # Place the 25th circle at the center of the square
+- centers[24] = [0.5, 0.5]
+-
+- # Place the 26th circle at a quarter-point for better overall balance
+- centers[25] = [0.25, 0.25]
++ # Place the 26th circle in a natural void of the grid (an interstitial site).
++ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
++ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
++ # minimizing local disruption.
++ centers[25] = [spacing, spacing]
+
+- # No need to clip centers, as they are intentionally placed within (0,1) range
+- # and compute_max_radii handles boundary constraints.
+-
+- # Compute maximum valid radii for this configuration
++ # Compute maximum valid radii for this superior configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence
+- for _ in range(200): # Iterate more times to stabilize radii
++ # Then, limit by distance to other circles using iterative proportional scaling.
++ # Increased iterations to ensure full convergence for a dense packing.
++ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+- if dist < 1e-9: # Handle cases where centers might be identical or very close
+- # If centers are practically the same, one circle must have radius 0
+- # or handle based on problem specifics (e.g., allow one small, one 0)
+- # For packing, it's safer to consider them as overlapping severely
+- # and shrink them
++ if dist < 1e-9:
++ # This case should not happen with this constructor, but it's a safe guard.
++ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+- # Scale both radii proportionally to just touch
++ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+- # If a full pass over all pairs results in no change, radii have stabilized
++ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..1d58f510d02dfdda2aae4dbcb65b43bbf7e33747
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/main.py
@@ -0,0 +1,104 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Compute maximum valid radii for this superior configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # Increased iterations to ensure full convergence for a dense packing.
+ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b2ee7b8d439ea3c13370e26d4c18f191154f4b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/original.py
@@ -0,0 +1,111 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 24 circles in a 6x4 grid
+ num_x_cells = 6
+ num_y_cells = 4
+
+ spacing_x = 1.0 / num_x_cells
+ spacing_y = 1.0 / num_y_cells
+
+ k = 0
+ for j in range(num_y_cells):
+ for i in range(num_x_cells):
+ centers[k, 0] = (i + 0.5) * spacing_x
+ centers[k, 1] = (j + 0.5) * spacing_y
+ k += 1
+
+ # Place the 25th circle at the center of the square
+ centers[24] = [0.5, 0.5]
+
+ # Place the 26th circle at a quarter-point for better overall balance
+ centers[25] = [0.25, 0.25]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(200): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # or handle based on problem specifics (e.g., allow one small, one 0)
+ # For packing, it's safer to consider them as overlapping severely
+ # and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..21fe72aa2085a0539c9445ca9bb416827e1114da
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_13/rewrite.txt
@@ -0,0 +1,95 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Compute maximum valid radii for this superior configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # Increased iterations to ensure full convergence for a dense packing.
+ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..c0933cb2516e35f4a9a0cf4fd9b63390d5639b4e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/edit.diff
@@ -0,0 +1,265 @@
+--- a/original.py
++++ b/original.py
+@@ -1,179 +1,191 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ Improved by combining insights from high-scoring prior programs:
+ - Optimized initial placement of the 26th circle.
+ - Reduced initial perturbation noise.
+ - Tuned learning rate for fine-grained adjustments.
+ - Increased iterations for radius calculation convergence.
+ - Robust handling of very close circle centers in radius calculation.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250 # Number of simulation steps (kept from current)
+ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+ wall_repulsion_strength = 0.5 # Kept from current
+
+- # --- Initialization ---
+- # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+- centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- centers[k, 0] = (i + 0.5) * spacing
+- centers[k, 1] = (j + 0.5) * spacing
+- k += 1
++ best_sum_radii = -1.0
++ best_final_centers = None
++ best_final_radii = None
+
+- # Place the 26th circle in a corner, as demonstrated effective in previous high-scoring
+- # constructor-based approaches (e.g., the 1.87 program). This efficiently uses boundary space.
+- centers[25] = [0.05, 0.05]
++ # Candidate initial positions for the 26th circle to optimize corner utilization.
++ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
++ candidate_extra_circle_initial_positions = [
++ [0.05, 0.05], # Original position
++ [0.05, 0.95], # Top-left corner
++ [0.95, 0.05], # Bottom-right corner
++ [0.95, 0.95], # Top-right corner
++ ]
+
+- # Removed random noise. Starting from a stable, high-performing grid
+- # and allowing the physics simulation to refine it seems more effective
+- # than adding initial perturbations.
++ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
++ # --- Initialization for current trial ---
++ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
++ # Re-initialize centers for each trial to ensure independent simulations.
++ current_centers = np.zeros((n, 2))
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for i in range(num_cells_side):
++ for j in range(num_cells_side):
++ current_centers[k, 0] = (i + 0.5) * spacing
++ current_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
+
+- # Removed initial clipping (e.g., to 0.01-0.99). The physics simulation's clipping
+- # and wall repulsion will naturally keep circles within bounds, allowing them to
+- # touch the actual 0 and 1 edges.
++ # Place the 26th circle at the current candidate position.
++ current_centers[25] = initial_pos_26th_circle
+
+- # --- Simulation Loop ---
+- for sim_iter in range(iterations): # Renamed loop variable to avoid conflict with `k`
+- # 1. Calculate radii for the current center configuration
+- radii = compute_max_radii(centers)
++ # Use a copy of centers for the simulation, as it modifies the array
++ centers_for_sim = current_centers.copy()
+
+- # 2. Calculate forces to push centers to a better configuration
+- forces = np.zeros((n, 2))
++ # --- Simulation Loop ---
++ for sim_iter in range(iterations):
++ # 1. Calculate radii for the current center configuration
++ radii = compute_max_radii(centers_for_sim)
+
+- # a. Circle-to-circle repulsion forces
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers[i] - centers[j]
+- dist = np.linalg.norm(vec)
++ # 2. Calculate forces to push centers to a better configuration
++ forces = np.zeros((n, 2))
+
+- # If distance is near zero, centers are effectively identical.
+- # Radii calculation in compute_max_radii will handle this robustly.
+- # Here, we primarily avoid division by zero if radii[i] + radii[j] is also near zero.
+- if dist < 1e-9:
+- continue
++ # a. Circle-to-circle repulsion forces
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers_for_sim[i] - centers_for_sim[j]
++ dist = np.linalg.norm(vec)
+
+- overlap = radii[i] + radii[j] - dist
++ if dist < 1e-9:
++ continue
+
+- if overlap > 0:
+- # Apply a force proportional to the overlap, along the vector between centers
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec
++ overlap = radii[i] + radii[j] - dist
+
+- # b. Wall repulsion forces
+- for i in range(n):
+- # Left wall
+- overlap_left = radii[i] - centers[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers[i, 0] + radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = radii[i] - centers[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers[i, 1] + radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
++ if overlap > 0:
++ force_magnitude = overlap
++ force_vec = (vec / dist) * force_magnitude
++ forces[i] += force_vec
++ forces[j] -= force_vec
+
+- # 3. Update center positions based on forces
+- # We adjust the learning rate over time (annealing) to help the system settle
+- current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+- centers += forces * current_lr
++ # b. Wall repulsion forces
++ for i in range(n):
++ # Left wall
++ overlap_left = radii[i] - centers_for_sim[i, 0]
++ if overlap_left > 0:
++ forces[i, 0] += wall_repulsion_strength * overlap_left
++ # Right wall
++ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
++ if overlap_right > 0:
++ forces[i, 0] -= wall_repulsion_strength * overlap_right
++ # Bottom wall
++ overlap_bottom = radii[i] - centers_for_sim[i, 1]
++ if overlap_bottom > 0:
++ forces[i, 1] += wall_repulsion_strength * overlap_bottom
++ # Top wall
++ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
++ if overlap_top > 0:
++ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+- # 4. Enforce boundary constraints - keep centers within [0,1]
+- centers = np.clip(centers, 0.0, 1.0)
++ # 3. Update center positions based on forces
++ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
++ centers_for_sim += forces * current_lr
+
+- # --- Finalization ---
+- # After the simulation, do a final radius calculation on the optimized centers
+- final_centers = centers
+- final_radii = compute_max_radii(final_centers)
++ # 4. Enforce boundary constraints - keep centers within [0,1]
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # --- Finalization for current trial ---
++ current_final_radii = compute_max_radii(centers_for_sim)
++ current_sum_radii = np.sum(current_final_radii)
++
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_final_centers = centers_for_sim.copy() # Store a copy
++ best_final_radii = current_final_radii.copy() # Store a copy
++
++ # Return the best configuration found across all trials
++ final_centers = best_final_centers
++ final_radii = best_final_radii
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae84d715fe232fbd4c97402fede1c9092e3d5047
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/main.py
@@ -0,0 +1,191 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250 # Number of simulation steps (kept from current)
+ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+ wall_repulsion_strength = 0.5 # Kept from current
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # Return the best configuration found across all trials
+ final_centers = best_final_centers
+ final_radii = best_final_radii
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..25edd58676011757a90b71ce889c54ebc86c8a3c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/original.py
@@ -0,0 +1,179 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250 # Number of simulation steps (kept from current)
+ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+ wall_repulsion_strength = 0.5 # Kept from current
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a corner, as demonstrated effective in previous high-scoring
+ # constructor-based approaches (e.g., the 1.87 program). This efficiently uses boundary space.
+ centers[25] = [0.05, 0.05]
+
+ # Removed random noise. Starting from a stable, high-performing grid
+ # and allowing the physics simulation to refine it seems more effective
+ # than adding initial perturbations.
+
+ # Removed initial clipping (e.g., to 0.01-0.99). The physics simulation's clipping
+ # and wall repulsion will naturally keep circles within bounds, allowing them to
+ # touch the actual 0 and 1 edges.
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations): # Renamed loop variable to avoid conflict with `k`
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ # If distance is near zero, centers are effectively identical.
+ # Radii calculation in compute_max_radii will handle this robustly.
+ # Here, we primarily avoid division by zero if radii[i] + radii[j] is also near zero.
+ if dist < 1e-9:
+ continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c0b979cfad5b01066210e69f581dcdb0bd94d606
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_14/search_replace.txt
@@ -0,0 +1,204 @@
+
+A major bottleneck in the current packing configuration is the placement of the 26th circle at `[0.05, 0.05]`, which forces it and the adjacent grid circle (`[0.1, 0.1]`) to be very small, as seen in the visualization. This edit implements a strategy to dynamically test multiple strategic initial positions for the 26th circle (specifically, the four corners of the unit square, slightly offset from the boundary). The full physics simulation is run for each candidate position, and the configuration yielding the highest total sum of radii is selected as the optimal result. This systematic exploration significantly improves the chances of finding a better initial condition that leverages available space more effectively, especially in the corners and edges, leading to a higher sum of radii.
+
+
+
+
+<<<<<<< SEARCH
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250 # Number of simulation steps (kept from current)
+ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+ wall_repulsion_strength = 0.5 # Kept from current
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a corner, as demonstrated effective in previous high-scoring
+ # constructor-based approaches (e.g., the 1.87 program). This efficiently uses boundary space.
+ centers[25] = [0.05, 0.05]
+
+ # Removed random noise. Starting from a stable, high-performing grid
+ # and allowing the physics simulation to refine it seems more effective
+ # than adding initial perturbations.
+
+ # Removed initial clipping (e.g., to 0.01-0.99). The physics simulation's clipping
+ # and wall repulsion will naturally keep circles within bounds, allowing them to
+ # touch the actual 0 and 1 edges.
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations): # Renamed loop variable to avoid conflict with `k`
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ # If distance is near zero, centers are effectively identical.
+ # Radii calculation in compute_max_radii will handle this robustly.
+ # Here, we primarily avoid division by zero if radii[i] + radii[j] is also near zero.
+ if dist < 1e-9:
+ continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+=======
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250 # Number of simulation steps (kept from current)
+ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+ wall_repulsion_strength = 0.5 # Kept from current
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # Return the best configuration found across all trials
+ final_centers = best_final_centers
+ final_radii = best_final_radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ffaa70d64a652559fea875997cad40e4039813f6
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..de4bb4bbabefd3763f81ef4958095d2cf4b57bcb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/edit.diff
@@ -0,0 +1,195 @@
+--- a/original.py
++++ b/original.py
+@@ -1,154 +1,164 @@
+ # EVOLVE-BLOCK-START
+ """
+-Physical simulation-based circle packing for n=26 circles.
+-This algorithm starts with a strong grid-based configuration and iteratively
+-refines circle positions to maximize the sum of radii.
++Improved physical simulation packing for n=26 circles.
++This algorithm starts with a more symmetric and promising grid-based
++configuration and uses an enhanced force model with "growth pressure"
++to actively seek out more optimal arrangements.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles by starting with a high-performing
+- grid layout and then simulating physical repulsion forces to find an
+- even more optimal arrangement of centers.
++ Constructs a packing of 26 circles by starting with an interstitial
++ grid layout and then simulating physical forces to find an optimal
++ arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- iterations = 250
+- # Reduced learning rate for fine-tuning a good initial configuration
+- learning_rate = 0.02
++ # More iterations to allow the system to settle into a fine-tuned state.
++ iterations = 500
++ # A smaller learning rate is used for careful refinement of a good initial guess.
++ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+-
+- # --- Initialization (Crossover from the high-scoring constructor) ---
+- # Start with the 5x5 grid which is a known strong configuration.
++ # A growth factor to create a "pressure" for circles to expand,
++ # generating repulsive forces even when they are just touching.
++ growth_pressure = 1.005
++
++ # --- Initialization ---
++ # Start with a 5x5 grid, a proven strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+- # Place the 26th circle in a corner to utilize that space.
+- centers[25] = [0.05, 0.05]
+
+- # No random noise is added. We want to refine this specific strong starting point.
++ # Place the 26th circle in the center of a grid cell. This is a
++ # more balanced starting point than a corner, as it doesn't
++ # immediately crowd any single circle. It mimics the known optimal
++ # structure for N=26.
++ centers[25] = [0.2, 0.2]
++
++ # No random noise is added. We want to deterministically refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+- for k in range(iterations):
++ for k_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
++
++ # Create "inflated" radii to generate repulsive forces even when just touching
++ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+- overlap = radii[i] + radii[j] - dist
++ # Use inflated radii to calculate overlap, creating a growth pressure
++ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+- # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+- # b. Wall repulsion forces
++ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+- overlap = radii[i] - centers[i, 0]
++ overlap = inflated_radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+- overlap = (centers[i, 0] + radii[i]) - 1.0
++ overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+- overlap = radii[i] - centers[i, 1]
++ overlap = inflated_radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+- overlap = (centers[i, 1] + radii[i]) - 1.0
++ overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+- current_lr = learning_rate * (1.0 - (k / iterations))**2
++ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- for _ in range(100):
++ # Increased iterations for better convergence.
++ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+- # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+- # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+- # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa667478dee645e6313153e219f5465b2e4e975a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/main.py
@@ -0,0 +1,164 @@
+# EVOLVE-BLOCK-START
+"""
+Improved physical simulation packing for n=26 circles.
+This algorithm starts with a more symmetric and promising grid-based
+configuration and uses an enhanced force model with "growth pressure"
+to actively seek out more optimal arrangements.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by starting with an interstitial
+ grid layout and then simulating physical forces to find an optimal
+ arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # More iterations to allow the system to settle into a fine-tuned state.
+ iterations = 500
+ # A smaller learning rate is used for careful refinement of a good initial guess.
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ # A growth factor to create a "pressure" for circles to expand,
+ # generating repulsive forces even when they are just touching.
+ growth_pressure = 1.005
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, a proven strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the center of a grid cell. This is a
+ # more balanced starting point than a corner, as it doesn't
+ # immediately crowd any single circle. It mimics the known optimal
+ # structure for N=26.
+ centers[25] = [0.2, 0.2]
+
+ # No random noise is added. We want to deterministically refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Use inflated radii to calculate overlap, creating a growth pressure
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap = inflated_radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = inflated_radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..789478cda723212de990bcede8fdc0eb4f963315
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/original.py
@@ -0,0 +1,154 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm starts with a strong grid-based configuration and iteratively
+refines circle positions to maximize the sum of radii.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by starting with a high-performing
+ grid layout and then simulating physical repulsion forces to find an
+ even more optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250
+ # Reduced learning rate for fine-tuning a good initial configuration
+ learning_rate = 0.02
+ wall_repulsion_strength = 0.5
+
+ # --- Initialization (Crossover from the high-scoring constructor) ---
+ # Start with the 5x5 grid which is a known strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in a corner to utilize that space.
+ centers[25] = [0.05, 0.05]
+
+ # No random noise is added. We want to refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap = radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..c111e488a3a2ba5d8989bfab87bea3253081bcd8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results
+Run 1/1 completed in 2.80 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.923173132299769
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1002, 0.8998)\n centers[5] = (0.3008, 0.0992)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.6983, 0.6998)\n centers[19] = (0.7000, 0.9002)\n centers[20] = (0.8999, 0.0994)\n centers[21] = (0.8996, 0.2998)\n centers[22] = (0.8996, 0.5010)\n centers[23] = (0.9017, 0.7002)\n centers[24] = (0.8996, 0.8996)\n centers[25] = (0.1992, 0.2008)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.923173132299769}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/packing_viz.png
+ execution_time_mean: 2.7994869123212993
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..656107eeb3a63481cd97bb2d507b30421aeaf508
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.923173132299769,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1002, 0.8998)\n centers[5] = (0.3008, 0.0992)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.6983, 0.6998)\n centers[19] = (0.7000, 0.9002)\n centers[20] = (0.8999, 0.0994)\n centers[21] = (0.8996, 0.2998)\n centers[22] = (0.8996, 0.5010)\n centers[23] = (0.9017, 0.7002)\n centers[24] = (0.8996, 0.8996)\n centers[25] = (0.1992, 0.2008)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.923173132299769
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/results/packing_viz.png",
+ "execution_time_mean": 2.7994869123212993,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..75cddae4a1392da2080ca0b9cde555315934d657
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_15/rewrite.txt
@@ -0,0 +1,155 @@
+# EVOLVE-BLOCK-START
+"""
+Improved physical simulation packing for n=26 circles.
+This algorithm starts with a more symmetric and promising grid-based
+configuration and uses an enhanced force model with "growth pressure"
+to actively seek out more optimal arrangements.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by starting with an interstitial
+ grid layout and then simulating physical forces to find an optimal
+ arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # More iterations to allow the system to settle into a fine-tuned state.
+ iterations = 500
+ # A smaller learning rate is used for careful refinement of a good initial guess.
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ # A growth factor to create a "pressure" for circles to expand,
+ # generating repulsive forces even when they are just touching.
+ growth_pressure = 1.005
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, a proven strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the center of a grid cell. This is a
+ # more balanced starting point than a corner, as it doesn't
+ # immediately crowd any single circle. It mimics the known optimal
+ # structure for N=26.
+ centers[25] = [0.2, 0.2]
+
+ # No random noise is added. We want to deterministically refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Use inflated radii to calculate overlap, creating a growth pressure
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap = inflated_radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = inflated_radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..fb732a7272aa3444f7932f046375c2fafad611a6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/edit.diff
@@ -0,0 +1,176 @@
+--- a/original.py
++++ b/original.py
+@@ -1,104 +1,121 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
++"""
++Hybrid circle packing for n=26 circles.
++Starts with a 5x5 grid plus a central circle and refines positions using
++force-directed relaxation, then computes radii iteratively.
++"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+- Constructs a specific arrangement of 26 circles in a unit square
+- based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
++ Constructs an arrangement of 26 circles using a force-directed
++ relaxation method, starting from a strong grid-based pattern,
++ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+- # Initialize arrays for 26 circles
+ n = 26
++ # Use a fixed seed for reproducibility, taken from the inspiration script.
++ np.random.seed(1337)
++
++ # 1. Initial Placement: Start with the superior 5x5 grid from the current program.
+ centers = np.zeros((n, 2))
+-
+- # --- New Initial Placement Strategy ---
+- # Arrange 25 circles in a dense and symmetric 5x5 grid.
+- # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+-
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+- # Place the 26th circle in a natural void of the grid (an interstitial site).
+- # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+- # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+- # minimizing local disruption.
+- centers[25] = [spacing, spacing]
+-
+- # Compute maximum valid radii for this superior configuration.
++ # Place the 26th circle in the center, a more symmetric void than [0.2, 0.2].
++ centers[25] = [0.5, 0.5]
++
++ # 2. Iterative Position Refinement (Force Simulation from the inspiration program).
++ # This allows the strong initial grid to relax into an even better configuration.
++ n_iter = 500 # Use a sufficient number of iterations for convergence.
++ step_size = 1e-4
++ wall_strength = 0.5
++
++ for _ in range(n_iter):
++ # Vectorized calculation of repulsion forces between all pairs of circles.
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dist_sq = np.sum(diffs**2, axis=-1)
++ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion.
++ inv_dist_sq = 1.0 / (dist_sq + 1e-8) # Add epsilon to avoid division by zero.
++ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
++ force_repel = np.sum(force_repel_matrix, axis=1)
++
++ # Wall repulsion force to keep circles within the square.
++ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
++ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
++
++ # Combine forces and update positions.
++ total_force = force_repel + wall_strength * force_wall
++ centers += step_size * total_force
++
++ # Enforce hard boundaries.
++ centers = np.clip(centers, 0.0, 1.0)
++
++ # 3. Compute optimal radii for the final, relaxed configuration.
+ radii = compute_max_radii(centers)
++
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
++ Compute the maximum possible radii for the given centers using an
++ iterative relaxation method until convergence. This is more robust.
++ (Method adopted from the inspiration script).
+ """
+ n = centers.shape[0]
+- radii = np.ones(n)
+
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
++ # Initialize radii based on the distance to the four walls.
++ radii = np.min([
++ centers[:, 0],
++ centers[:, 1],
++ 1 - centers[:, 0],
++ 1 - centers[:, 1]
++ ], axis=0)
+
+- # Then, limit by distance to other circles using iterative proportional scaling.
+- # Increased iterations to ensure full convergence for a dense packing.
+- for _ in range(500): # Iterate more times to stabilize radii
+- updated_in_pass = False
++ # Iteratively shrink any overlapping circles until the layout is stable.
++ max_iter = 500 # Safety break for the convergence loop.
++ for _ in range(max_iter):
++ changed_in_iter = False
++ # Check every pair of circles for overlap.
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
++ sum_r = radii[i] + radii[j]
+
+- # If current radii would cause overlap and distance is not zero
+- if dist < 1e-9:
+- # This case should not happen with this constructor, but it's a safe guard.
+- # If centers are identical, their radii must be zero.
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
+- continue
++ if sum_r > dist:
++ # Overlap detected. Shrink radii proportionally to their size.
++ overlap = sum_r - dist
++ if sum_r > 1e-9: # Avoid division by zero.
++ radii[i] -= overlap * (radii[i] / sum_r)
++ radii[j] -= overlap * (radii[j] / sum_r)
++ changed_in_iter = True
+
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- # Scale both radii proportionally so they just touch
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- # If a full pass over all pairs results in no change, radii have stabilized.
+- if not updated_in_pass:
++ if not changed_in_iter:
++ # If a full pass completes with no changes, radii have converged.
+ break
+-
++
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f2006a74686117e7bf26a42eff28e291d5a70bc
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/main.py
@@ -0,0 +1,121 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid circle packing for n=26 circles.
+Starts with a 5x5 grid plus a central circle and refines positions using
+force-directed relaxation, then computes radii iteratively.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method, starting from a strong grid-based pattern,
+ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility, taken from the inspiration script.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with the superior 5x5 grid from the current program.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the center, a more symmetric void than [0.2, 0.2].
+ centers[25] = [0.5, 0.5]
+
+ # 2. Iterative Position Refinement (Force Simulation from the inspiration program).
+ # This allows the strong initial grid to relax into an even better configuration.
+ n_iter = 500 # Use a sufficient number of iterations for convergence.
+ step_size = 1e-4
+ wall_strength = 0.5
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles.
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion.
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8) # Add epsilon to avoid division by zero.
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force to keep circles within the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions.
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries.
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final, relaxed configuration.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust.
+ (Method adopted from the inspiration script).
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop.
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap.
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero.
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, radii have converged.
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..1d58f510d02dfdda2aae4dbcb65b43bbf7e33747
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/original.py
@@ -0,0 +1,104 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Compute maximum valid radii for this superior configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # Increased iterations to ensure full convergence for a dense packing.
+ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e5a5280e366d38156176f2249fedf477566bcb8a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_16/rewrite.txt
@@ -0,0 +1,112 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid circle packing for n=26 circles.
+Starts with a 5x5 grid plus a central circle and refines positions using
+force-directed relaxation, then computes radii iteratively.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method, starting from a strong grid-based pattern,
+ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility, taken from the inspiration script.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with the superior 5x5 grid from the current program.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the center, a more symmetric void than [0.2, 0.2].
+ centers[25] = [0.5, 0.5]
+
+ # 2. Iterative Position Refinement (Force Simulation from the inspiration program).
+ # This allows the strong initial grid to relax into an even better configuration.
+ n_iter = 500 # Use a sufficient number of iterations for convergence.
+ step_size = 1e-4
+ wall_strength = 0.5
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles.
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion.
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8) # Add epsilon to avoid division by zero.
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force to keep circles within the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions.
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries.
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final, relaxed configuration.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust.
+ (Method adopted from the inspiration script).
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop.
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap.
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero.
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, radii have converged.
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6c340a937b3bd64387e84919e0d0777a5a525112
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..47a16d2b918872980c57783e2886f1ff4b10ffef
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/edit.diff
@@ -0,0 +1,186 @@
+--- a/original.py
++++ b/original.py
+@@ -1,111 +1,121 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
++"""
++Hybrid circle packing for n=26 circles.
++Starts with a high-quality 5x5 grid constructor and refines positions
++using force-directed relaxation, then computes radii iteratively.
++"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+- Construct a specific arrangement of 26 circles in a unit square
+- that attempts to maximize the sum of their radii.
++ Constructs an optimized arrangement of 26 circles by first placing them
++ in a strong grid-based pattern and then refining their positions using
++ a force-directed simulation.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+- # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+- # --- New Initial Placement Strategy ---
+- # Arrange 24 circles in a 6x4 grid
+- num_x_cells = 6
+- num_y_cells = 4
+-
+- spacing_x = 1.0 / num_x_cells
+- spacing_y = 1.0 / num_y_cells
++ # 1. Initial Placement: Use the superior 5x5 grid + interstitial circle constructor.
++ # This provides a much more stable and efficient starting point than a 6x4 grid.
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
+
+ k = 0
+- for j in range(num_y_cells):
+- for i in range(num_x_cells):
+- centers[k, 0] = (i + 0.5) * spacing_x
+- centers[k, 1] = (j + 0.5) * spacing_y
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ centers[k, 0] = (i + 0.5) * spacing
++ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
++
++ # Place the 26th circle in a natural void of the grid.
++ centers[25] = [spacing, spacing] # Position (0.2, 0.2)
+
+- # Place the 25th circle at the center of the square
+- centers[24] = [0.5, 0.5]
++ # 2. Iterative Position Refinement (Force Simulation from inspiration program)
++ # This takes the excellent static layout and optimizes it dynamically.
++ n_iter = 500 # More iterations for better convergence
++ step_size = 1e-4 # Small step size for fine-tuning from a good start
++ wall_strength = 0.5
+
+- # Place the 26th circle at a quarter-point for better overall balance
+- centers[25] = [0.25, 0.25]
+-
+- # No need to clip centers, as they are intentionally placed within (0,1) range
+- # and compute_max_radii handles boundary constraints.
++ for _ in range(n_iter):
++ # Vectorized calculation of repulsion forces between all pairs of circles
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dist_sq = np.sum(diffs**2, axis=-1)
++ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
++ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
++ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
++ force_repel = np.sum(force_repel_matrix, axis=1)
+
+- # Compute maximum valid radii for this configuration
++ # Wall repulsion force to keep circles within the square
++ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
++ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
++
++ # Combine forces and update positions
++ total_force = force_repel + wall_strength * force_wall
++ centers += step_size * total_force
++
++ # Enforce hard boundaries
++ centers = np.clip(centers, 0.0, 1.0)
++
++ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
++
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
++ Compute the maximum possible radii for the given centers using an
++ iterative relaxation method until convergence. This is a robust method
++ taken from the inspiration program.
+ """
+ n = centers.shape[0]
+- radii = np.ones(n)
+
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
++ # Initialize radii based on the distance to the four walls.
++ radii = np.min([
++ centers[:, 0],
++ centers[:, 1],
++ 1 - centers[:, 0],
++ 1 - centers[:, 1]
++ ], axis=0)
+
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence
+- for _ in range(200): # Iterate more times to stabilize radii
+- updated_in_pass = False
++ # Iteratively shrink any overlapping circles until the layout is stable.
++ max_iter = 500 # Safety break for the convergence loop
++ for _ in range(max_iter):
++ changed_in_iter = False
++ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
++ sum_r = radii[i] + radii[j]
+
+- # If current radii would cause overlap and distance is not zero
+- if dist < 1e-9: # Handle cases where centers might be identical or very close
+- # If centers are practically the same, one circle must have radius 0
+- # or handle based on problem specifics (e.g., allow one small, one 0)
+- # For packing, it's safer to consider them as overlapping severely
+- # and shrink them
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
+- continue
++ if sum_r > dist:
++ # Overlap detected. Shrink radii proportionally to their size.
++ overlap = sum_r - dist
++ if sum_r > 1e-9: # Avoid division by zero
++ radii[i] -= overlap * (radii[i] / sum_r)
++ radii[j] -= overlap * (radii[j] / sum_r)
++ changed_in_iter = True
+
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- # Scale both radii proportionally to just touch
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- # If a full pass over all pairs results in no change, radii have stabilized
+- if not updated_in_pass:
++ if not changed_in_iter:
++ # If a full pass completes with no changes, the radii have converged.
+ break
+-
+- return np.maximum(radii, 0) # Ensure no negative radii
++
++ return radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..97a35b8ade7d62f0076a9ad92394bc399aaf91ad
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/main.py
@@ -0,0 +1,121 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid circle packing for n=26 circles.
+Starts with a high-quality 5x5 grid constructor and refines positions
+using force-directed relaxation, then computes radii iteratively.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an optimized arrangement of 26 circles by first placing them
+ in a strong grid-based pattern and then refining their positions using
+ a force-directed simulation.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # 1. Initial Placement: Use the superior 5x5 grid + interstitial circle constructor.
+ # This provides a much more stable and efficient starting point than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid.
+ centers[25] = [spacing, spacing] # Position (0.2, 0.2)
+
+ # 2. Iterative Position Refinement (Force Simulation from inspiration program)
+ # This takes the excellent static layout and optimizes it dynamically.
+ n_iter = 500 # More iterations for better convergence
+ step_size = 1e-4 # Small step size for fine-tuning from a good start
+ wall_strength = 0.5
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force to keep circles within the square
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is a robust method
+ taken from the inspiration program.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b2ee7b8d439ea3c13370e26d4c18f191154f4b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/original.py
@@ -0,0 +1,111 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 24 circles in a 6x4 grid
+ num_x_cells = 6
+ num_y_cells = 4
+
+ spacing_x = 1.0 / num_x_cells
+ spacing_y = 1.0 / num_y_cells
+
+ k = 0
+ for j in range(num_y_cells):
+ for i in range(num_x_cells):
+ centers[k, 0] = (i + 0.5) * spacing_x
+ centers[k, 1] = (j + 0.5) * spacing_y
+ k += 1
+
+ # Place the 25th circle at the center of the square
+ centers[24] = [0.5, 0.5]
+
+ # Place the 26th circle at a quarter-point for better overall balance
+ centers[25] = [0.25, 0.25]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(200): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # or handle based on problem specifics (e.g., allow one small, one 0)
+ # For packing, it's safer to consider them as overlapping severely
+ # and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..bf32430b1cd2b4ba43994e1beceb040baa7334b3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results
+Run 1/1 completed in 0.06 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.075707553429705
+ public: {'centers_str': ' centers[0] = (0.0130, 0.0130)\n centers[1] = (0.2634, 0.0220)\n centers[2] = (0.5965, 0.0190)\n centers[3] = (0.8359, 0.0221)\n centers[4] = (0.9790, 0.0140)\n centers[5] = (0.0220, 0.2634)\n centers[6] = (0.1183, 0.1183)\n centers[7] = (0.5256, 0.0980)\n centers[8] = (0.9785, 0.0829)\n centers[9] = (0.9749, 0.2217)\n centers[10] = (0.0190, 0.5965)\n centers[11] = (0.0980, 0.5256)\n centers[12] = (0.5376, 0.5376)\n centers[13] = (0.9735, 0.6107)\n centers[14] = (0.9733, 0.4199)\n centers[15] = (0.0221, 0.8359)\n centers[16] = (0.0829, 0.9785)\n centers[17] = (0.6107, 0.9735)\n centers[18] = (0.9460, 0.9460)\n centers[19] = (0.9768, 0.8077)\n centers[20] = (0.0140, 0.9790)\n centers[21] = (0.2217, 0.9749)\n centers[22] = (0.4199, 0.9733)\n centers[23] = (0.8077, 0.9768)\n centers[24] = (0.9856, 0.9856)\n centers[25] = (0.0463, 0.0463)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.075707553429705}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/packing_viz.png
+ execution_time_mean: 0.06448892876505852
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..36fc4c1fc2146c3d12ede71d1f605ba5b799e673
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.075707553429705,
+ "public": {
+ "centers_str": " centers[0] = (0.0130, 0.0130)\n centers[1] = (0.2634, 0.0220)\n centers[2] = (0.5965, 0.0190)\n centers[3] = (0.8359, 0.0221)\n centers[4] = (0.9790, 0.0140)\n centers[5] = (0.0220, 0.2634)\n centers[6] = (0.1183, 0.1183)\n centers[7] = (0.5256, 0.0980)\n centers[8] = (0.9785, 0.0829)\n centers[9] = (0.9749, 0.2217)\n centers[10] = (0.0190, 0.5965)\n centers[11] = (0.0980, 0.5256)\n centers[12] = (0.5376, 0.5376)\n centers[13] = (0.9735, 0.6107)\n centers[14] = (0.9733, 0.4199)\n centers[15] = (0.0221, 0.8359)\n centers[16] = (0.0829, 0.9785)\n centers[17] = (0.6107, 0.9735)\n centers[18] = (0.9460, 0.9460)\n centers[19] = (0.9768, 0.8077)\n centers[20] = (0.0140, 0.9790)\n centers[21] = (0.2217, 0.9749)\n centers[22] = (0.4199, 0.9733)\n centers[23] = (0.8077, 0.9768)\n centers[24] = (0.9856, 0.9856)\n centers[25] = (0.0463, 0.0463)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.075707553429705
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/results/packing_viz.png",
+ "execution_time_mean": 0.06448892876505852,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5e9c0bb221514aa5e3003798ee6d94b81188a4e3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_17/rewrite.txt
@@ -0,0 +1,112 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid circle packing for n=26 circles.
+Starts with a high-quality 5x5 grid constructor and refines positions
+using force-directed relaxation, then computes radii iteratively.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an optimized arrangement of 26 circles by first placing them
+ in a strong grid-based pattern and then refining their positions using
+ a force-directed simulation.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # 1. Initial Placement: Use the superior 5x5 grid + interstitial circle constructor.
+ # This provides a much more stable and efficient starting point than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid.
+ centers[25] = [spacing, spacing] # Position (0.2, 0.2)
+
+ # 2. Iterative Position Refinement (Force Simulation from inspiration program)
+ # This takes the excellent static layout and optimizes it dynamically.
+ n_iter = 500 # More iterations for better convergence
+ step_size = 1e-4 # Small step size for fine-tuning from a good start
+ wall_strength = 0.5
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force to keep circles within the square
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is a robust method
+ taken from the inspiration program.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d361271b22977dc3007922d5d050e3368709d5b6
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..409f1163a6a9c04d064e572fa38116af5801bd34
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/edit.diff
@@ -0,0 +1,166 @@
+--- a/original.py
++++ b/original.py
+@@ -1,111 +1,139 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+- centers = np.zeros((n, 2))
+
+- # --- New Initial Placement Strategy ---
+- # Arrange 24 circles in a 6x4 grid
+- num_x_cells = 6
+- num_y_cells = 4
++ # --- Refined Initial Placement Strategy ---
++ # Arrange 25 circles in a 5x5 grid.
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+- spacing_x = 1.0 / num_x_cells
+- spacing_y = 1.0 / num_y_cells
+-
++ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+- for j in range(num_y_cells):
+- for i in range(num_x_cells):
+- centers[k, 0] = (i + 0.5) * spacing_x
+- centers[k, 1] = (j + 0.5) * spacing_y
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
++ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+- # Place the 25th circle at the center of the square
+- centers[24] = [0.5, 0.5]
++ # Define strategic candidate placements for the 26th circle.
++ # These are interstitial points and the center of the square.
++ # The interstitial points are derived from the 5x5 grid's structure.
++ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
++ candidate_extra_positions = [
++ [spacing, spacing], # (0.2, 0.2)
++ [spacing, 0.5], # (0.2, 0.5)
++ [spacing, 1 - spacing], # (0.2, 0.8)
++ [0.5, spacing], # (0.5, 0.2)
++ [0.5, 0.5], # (0.5, 0.5) - Center
++ [0.5, 1 - spacing], # (0.5, 0.8)
++ [1 - spacing, spacing], # (0.8, 0.2)
++ [1 - spacing, 0.5], # (0.8, 0.5)
++ [1 - spacing, 1 - spacing] # (0.8, 0.8)
++ ]
+
+- # Place the 26th circle at a quarter-point for better overall balance
+- centers[25] = [0.25, 0.25]
++ best_sum_radii = 0
++ best_centers = None
++ best_radii = None
+
+- # No need to clip centers, as they are intentionally placed within (0,1) range
+- # and compute_max_radii handles boundary constraints.
++ # Iterate through candidate positions for the 26th circle to find the best one.
++ for extra_pos in candidate_extra_positions:
++ current_centers = np.zeros((n, 2))
++ current_centers[:25] = base_centers
++ current_centers[25] = extra_pos
+
+- # Compute maximum valid radii for this configuration
+- radii = compute_max_radii(centers)
++ current_radii = compute_max_radii(current_centers)
++ current_sum_radii = np.sum(current_radii)
++
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_centers = current_centers
++ best_radii = current_radii
++
++ # Fallback if no valid configuration was found (should not happen with good candidates)
++ if best_centers is None:
++ centers = np.zeros((n, 2))
++ centers[:25] = base_centers
++ centers[25] = [0.5, 0.5] # Default to center for 26th circle
++ radii = compute_max_radii(centers)
++ else:
++ centers = best_centers
++ radii = best_radii
++
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+- for _ in range(200): # Iterate more times to stabilize radii
++ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+- # or handle based on problem specifics (e.g., allow one small, one 0)
+- # For packing, it's safer to consider them as overlapping severely
+- # and shrink them
++ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c62f05af02e898eb2fbc02f6d58c10848f6776b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/main.py
@@ -0,0 +1,139 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers
+ current_centers[25] = extra_pos
+
+ current_radii = compute_max_radii(current_centers)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = current_centers
+ best_radii = current_radii
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ centers = np.zeros((n, 2))
+ centers[:25] = base_centers
+ centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ radii = compute_max_radii(centers)
+ else:
+ centers = best_centers
+ radii = best_radii
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c8f46246c4d4e071406c52579bba30f7bb841ba
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/original.py
@@ -0,0 +1,111 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 24 circles in a 6x4 grid
+ num_x_cells = 6
+ num_y_cells = 4
+
+ spacing_x = 1.0 / num_x_cells
+ spacing_y = 1.0 / num_y_cells
+
+ k = 0
+ for j in range(num_y_cells):
+ for i in range(num_x_cells):
+ centers[k, 0] = (i + 0.5) * spacing_x
+ centers[k, 1] = (j + 0.5) * spacing_y
+ k += 1
+
+ # Place the 25th circle at the center of the square
+ centers[24] = [0.5, 0.5]
+
+ # Place the 26th circle at a quarter-point for better overall balance
+ centers[25] = [0.25, 0.25]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(200): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # or handle based on problem specifics (e.g., allow one small, one 0)
+ # For packing, it's safer to consider them as overlapping severely
+ # and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..f7b62c9c02c0fc8401690b1aa0602d3b41f19665
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results
+Run 1/1 completed in 0.67 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9169561144739513
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.9000, 0.1000)\n centers[5] = (0.1000, 0.3000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.5000, 0.7000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.9000, 0.7000)\n centers[20] = (0.1000, 0.9000)\n centers[21] = (0.3000, 0.9000)\n centers[22] = (0.5000, 0.9000)\n centers[23] = (0.7000, 0.9000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.2000, 0.2000)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9169561144739513}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/packing_viz.png
+ execution_time_mean: 0.6694444781169295
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..4bd08ac07c5bac5de3f1f2ac32859b77c93219b6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9169561144739513,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.9000, 0.1000)\n centers[5] = (0.1000, 0.3000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.5000, 0.7000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.9000, 0.7000)\n centers[20] = (0.1000, 0.9000)\n centers[21] = (0.3000, 0.9000)\n centers[22] = (0.5000, 0.9000)\n centers[23] = (0.7000, 0.9000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.2000, 0.2000)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9169561144739513
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/results/packing_viz.png",
+ "execution_time_mean": 0.6694444781169295,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..38ccd9a98296d081ef845b0be452033d83ab95cd
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_18/search_replace.txt
@@ -0,0 +1,213 @@
+
+grid_and_dynamic_placement_and_iter_count
+
+
+
+This edit significantly refines the initial placement strategy and the radius computation convergence.
+
+1. **Switch to a 5x5 Grid for 25 Circles:** The previous 6x4 grid (24 circles) resulted in an uneven distribution and non-square cells, which is generally suboptimal for square packing. A 5x5 grid (25 circles) offers a more symmetric and denser initial configuration.
+2. **Dynamic Placement of the 26th Circle:** Instead of a fixed, potentially suboptimal position, the 26th circle's placement is now dynamically optimized. The code iterates through a set of nine strategic interstitial and central candidate locations (e.g., [0.2, 0.2], [0.5, 0.5], [0.8, 0.8], etc.) derived from the 5x5 grid. For each candidate, the `compute_max_radii` function is called, and the configuration yielding the highest total sum of radii is selected. This allows the system to find a locally optimal position for the last circle within a set of promising voids.
+3. **Increased Iterations for Radius Convergence:** The number of iterations in `compute_max_radii` has been increased from 200 to 500. This ensures that the iterative proportional scaling method has more time to converge, leading to more accurate and potentially higher radii for a given center configuration. This is crucial as the packing becomes denser.
+
+
+
+<<<<<<< SEARCH
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 24 circles in a 6x4 grid
+ num_x_cells = 6
+ num_y_cells = 4
+
+ spacing_x = 1.0 / num_x_cells
+ spacing_y = 1.0 / num_y_cells
+
+ k = 0
+ for j in range(num_y_cells):
+ for i in range(num_x_cells):
+ centers[k, 0] = (i + 0.5) * spacing_x
+ centers[k, 1] = (j + 0.5) * spacing_y
+ k += 1
+
+ # Place the 25th circle at the center of the square
+ centers[24] = [0.5, 0.5]
+
+ # Place the 26th circle at a quarter-point for better overall balance
+ centers[25] = [0.25, 0.25]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(200): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # or handle based on problem specifics (e.g., allow one small, one 0)
+ # For packing, it's safer to consider them as overlapping severely
+ # and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+=======
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers
+ current_centers[25] = extra_pos
+
+ current_radii = compute_max_radii(current_centers)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = current_centers
+ best_radii = current_radii
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ centers = np.zeros((n, 2))
+ centers[:25] = base_centers
+ centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ radii = compute_max_radii(centers)
+ else:
+ centers = best_centers
+ radii = best_radii
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..29f7d4bf98a1c32b246874e7fc85e0e4dfee5ec8
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..c25f52665f2ec28783cca267958758389b8cf8f2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/edit.diff
@@ -0,0 +1,385 @@
+--- a/original.py
++++ b/original.py
+@@ -1,164 +1,242 @@
+ # EVOLVE-BLOCK-START
+-"""
+-Improved physical simulation packing for n=26 circles.
+-This algorithm starts with a more symmetric and promising grid-based
+-configuration and uses an enhanced force model with "growth pressure"
+-to actively seek out more optimal arrangements.
+-"""
+-
+-import numpy as np
++class CirclePacker:
++ """
++ Encapsulates the state and logic for packing N circles in a unit square
++ using a force-directed simulation.
++
++ Attributes:
++ n_circles (int): The number of circles to pack.
++ config (dict): A dictionary containing hyperparameters for the simulation.
++ centers (np.array): Current (x, y) coordinates of circle centers.
++ radii (np.array): Current radii of circles.
++ """
++
++ def __init__(self, n_circles, config):
++ """
++ Initializes the CirclePacker with the number of circles and configuration.
++
++ Args:
++ n_circles (int): The number of circles.
++ config (dict): Hyperparameters for the simulation.
++ """
++ self.n_circles = n_circles
++ self.config = config
++
++ self.centers = np.zeros((self.n_circles, 2))
++ self.radii = np.zeros(self.n_circles)
++
++ self._initialize_centers()
++
++ def _initialize_centers(self):
++ """
++ Initializes the center positions of the circles based on a grid pattern
++ and a specific placement for the extra circle.
++ """
++ grid_size = self.config["grid_size"]
++ spacing = 1.0 / grid_size
++
++ k = 0
++ for i in range(grid_size):
++ for j in range(grid_size):
++ # Fill up to N-1 circles in the grid
++ if k < self.n_circles - 1:
++ self.centers[k, 0] = (i + 0.5) * spacing
++ self.centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++ else:
++ break # All grid spots for N-1 circles are filled
++ if k == self.n_circles - 1:
++ break # All grid spots for N-1 circles are filled
++
++ # Place the last circle at the specified interstitial position
++ # For n=26, grid_size=5 means 25 circles are placed, so the 26th is centers[25]
++ self.centers[self.n_circles - 1] = self.config["extra_circle_initial_pos"]
++
++ def _compute_max_radii(self):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ This uses an iterative proportional scaling method.
++
++ Returns:
++ np.array: An array of radii for each circle.
++ """
++ radii = np.ones(self.n_circles)
++
++ # First, limit by distance to square borders
++ for i in range(self.n_circles):
++ x, y = self.centers[i]
++ radii[i] = min(x, y, 1 - x, 1 - y)
++
++ # Then, limit by distance to other circles using iterative proportional scaling
++ for _ in range(self.config["radii_iterations"]):
++ updated_in_pass = False
++ for i in range(self.n_circles):
++ for j in range(i + 1, self.n_circles):
++ dist = np.linalg.norm(self.centers[i] - self.centers[j])
++
++ # Only adjust if there's an overlap and circles are not at identical positions
++ if radii[i] + radii[j] > dist and dist > 1e-9:
++ updated_in_pass = True
++ # Scale both radii proportionally to just touch
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++
++ # If a full pass over all pairs results in no change, radii have stabilized
++ if not updated_in_pass:
++ break
++
++ return np.maximum(radii, 0.0) # Ensure no negative radii
++
++ def _calculate_forces(self, current_radii):
++ """
++ Calculates repulsion forces between circles and from walls.
++ Uses inflated radii for a "growth pressure" effect.
++
++ Args:
++ current_radii (np.array): The current radii of the circles.
++
++ Returns:
++ np.array: An array of (dx, dy) forces for each circle.
++ """
++ forces = np.zeros((self.n_circles, 2))
++ growth_pressure = self.config["growth_pressure"]
++ wall_repulsion_strength = self.config["wall_repulsion_strength"]
++
++ inflated_radii = current_radii * growth_pressure
++
++ # a. Circle-to-circle repulsion forces (vectorized)
++ # Calculate differences between all pairs of centers (N, N, 2)
++ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
++
++ # Calculate squared distances (N, N)
++ dist_sq = np.sum(diffs**2, axis=-1)
++
++ # Calculate distances (N, N), add epsilon for numerical stability
++ dist = np.sqrt(dist_sq + 1e-9)
++
++ # Calculate sum of inflated radii for all pairs (N, N)
++ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
++
++ # Calculate overlap for all pairs (N, N)
++ overlap_matrix = sum_inflated_radii - dist
++
++ # Only consider positive overlap (repulsion) and prevent self-repulsion
++ np.fill_diagonal(overlap_matrix, 0)
++ overlap_matrix[overlap_matrix < 0] = 0
++
++ # Calculate unit vectors for force direction (N, N, 2)
++ # Use np.errstate to suppress division warnings for very small distances
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vecs = diffs / dist[:, :, np.newaxis]
++ # Set force direction to zero if distance is negligible to prevent NaN/Inf
++ unit_vecs[dist < 1e-9] = 0
++
++ # Force magnitudes are proportional to overlap (N, N)
++ force_magnitudes = overlap_matrix
++
++ # Total forces on each circle from all others (N, 2)
++ # Sum forces applied BY other circles (axis=1) onto the current circle
++ forces += np.sum(unit_vecs * force_magnitudes[:, :, np.newaxis], axis=1)
++
++
++ # b. Wall repulsion forces (vectorized)
++ x_centers, y_centers = self.centers[:, 0], self.centers[:, 1]
++ r_inflated = inflated_radii
++
++ # Overlap with all four walls for all circles
++ overlap_left = r_inflated - x_centers
++ overlap_right = x_centers + r_inflated - 1.0
++ overlap_bottom = r_inflated - y_centers
++ overlap_top = y_centers + r_inflated - 1.0
++
++ # Apply forces only for positive overlaps
++ forces[:, 0] += wall_repulsion_strength * np.where(overlap_left > 0, overlap_left, 0)
++ forces[:, 0] -= wall_repulsion_strength * np.where(overlap_right > 0, overlap_right, 0)
++ forces[:, 1] += wall_repulsion_strength * np.where(overlap_bottom > 0, overlap_bottom, 0)
++ forces[:, 1] -= wall_repulsion_strength * np.where(overlap_top > 0, overlap_top, 0)
++
++ return forces
++
++ def _update_positions(self, forces, current_lr):
++ """
++ Updates circle positions based on calculated forces and learning rate,
++ clipping them to stay within the unit square.
++
++ Args:
++ forces (np.array): The forces acting on each circle.
++ current_lr (float): The current learning rate for position updates.
++ """
++ self.centers += forces * current_lr
++ self.centers = np.clip(self.centers, 0.0, 1.0) # Ensure centers stay within [0,1]
++
++ def simulate(self):
++ """
++ Runs the full physical simulation to optimize circle positions.
++ """
++ iterations = self.config["iterations"]
++ learning_rate = self.config["learning_rate"]
++
++ for k_iter in range(iterations):
++ current_radii = self._compute_max_radii()
++ forces = self._calculate_forces(current_radii)
++
++ # Annealing learning rate: decreases over time to help the system settle
++ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
++ self._update_positions(forces, current_lr)
++
++ # Final calculation of radii after stabilization to get the best fit
++ self.radii = self._compute_max_radii()
++
++ def get_results(self):
++ """
++ Returns the optimized circle centers and radii.
++
++ Returns:
++ tuple: A tuple containing:
++ - np.array: The final optimized centers.
++ - np.array: The final radii corresponding to the centers.
++ """
++ return self.centers, self.radii
++
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles by starting with an interstitial
+- grid layout and then simulating physical forces to find an optimal
+- arrangement of centers.
++ Constructs a packing of 26 circles using the CirclePacker class.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+- # --- Hyperparameters for the simulation ---
+- # More iterations to allow the system to settle into a fine-tuned state.
+- iterations = 500
+- # A smaller learning rate is used for careful refinement of a good initial guess.
+- learning_rate = 0.01
+- wall_repulsion_strength = 0.5
+- # A growth factor to create a "pressure" for circles to expand,
+- # generating repulsive forces even when they are just touching.
+- growth_pressure = 1.005
+-
+- # --- Initialization ---
+- # Start with a 5x5 grid, a proven strong configuration.
+- centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- centers[k, 0] = (i + 0.5) * spacing
+- centers[k, 1] = (j + 0.5) * spacing
+- k += 1
++ # Define hyperparameters for the simulation
++ config = {
++ "n_circles": n,
++ "iterations": 500, # Number of simulation steps
++ "learning_rate": 0.01, # Controls how much positions change per step
++ "wall_repulsion_strength": 0.5, # Strength of forces from boundaries
++ "growth_pressure": 1.005, # Factor to inflate radii for repulsion, encouraging expansion
++ "grid_size": 5, # For the initial 5x5 grid arrangement
++ "extra_circle_initial_pos": [0.2, 0.2], # Optimized initial position for the 26th circle
++ "radii_iterations": 250, # Iterations for the `_compute_max_radii` function
++ }
++
++ # Instantiate and run the CirclePacker simulation
++ packer = CirclePacker(n, config)
++ packer.simulate()
+
+- # Place the 26th circle in the center of a grid cell. This is a
+- # more balanced starting point than a corner, as it doesn't
+- # immediately crowd any single circle. It mimics the known optimal
+- # structure for N=26.
+- centers[25] = [0.2, 0.2]
+-
+- # No random noise is added. We want to deterministically refine this specific strong starting point.
+-
+- # --- Simulation Loop ---
+- for k_iter in range(iterations):
+- # 1. Calculate radii for the current center configuration
+- radii = compute_max_radii(centers)
+-
+- # 2. Calculate forces to push centers to a better configuration
+- forces = np.zeros((n, 2))
+-
+- # Create "inflated" radii to generate repulsive forces even when just touching
+- inflated_radii = radii * growth_pressure
+-
+- # a. Circle-to-circle repulsion forces
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers[i] - centers[j]
+- dist = np.linalg.norm(vec)
+- if dist < 1e-9: continue
+-
+- # Use inflated radii to calculate overlap, creating a growth pressure
+- overlap = inflated_radii[i] + inflated_radii[j] - dist
+-
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec
+-
+- # b. Wall repulsion forces, also using inflated radii
+- for i in range(n):
+- # Left wall
+- overlap = inflated_radii[i] - centers[i, 0]
+- if overlap > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap
+- # Right wall
+- overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+- if overlap > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap
+- # Bottom wall
+- overlap = inflated_radii[i] - centers[i, 1]
+- if overlap > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap
+- # Top wall
+- overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+- if overlap > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap
+-
+- # 3. Update center positions based on forces
+- # We adjust the learning rate over time (annealing) to help the system settle
+- current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+- centers += forces * current_lr
+-
+- # 4. Enforce boundary constraints
+- centers = np.clip(centers, 0.0, 1.0)
+-
+- # --- Finalization ---
+- # After the simulation, do a final radius calculation on the optimized centers
+- final_centers = centers
+- final_radii = compute_max_radii(final_centers)
+-
+- return final_centers, final_radii
+-
+-
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This uses an iterative proportional scaling method.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence.
+- for _ in range(250):
+- updated_in_pass = False
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if radii[i] + radii[j] > dist and dist > 1e-9:
+- updated_in_pass = True
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0)
++ return packer.get_results()
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..57ee241cc3b6660934398b843de518909a473175
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/main.py
@@ -0,0 +1,242 @@
+# EVOLVE-BLOCK-START
+class CirclePacker:
+ """
+ Encapsulates the state and logic for packing N circles in a unit square
+ using a force-directed simulation.
+
+ Attributes:
+ n_circles (int): The number of circles to pack.
+ config (dict): A dictionary containing hyperparameters for the simulation.
+ centers (np.array): Current (x, y) coordinates of circle centers.
+ radii (np.array): Current radii of circles.
+ """
+
+ def __init__(self, n_circles, config):
+ """
+ Initializes the CirclePacker with the number of circles and configuration.
+
+ Args:
+ n_circles (int): The number of circles.
+ config (dict): Hyperparameters for the simulation.
+ """
+ self.n_circles = n_circles
+ self.config = config
+
+ self.centers = np.zeros((self.n_circles, 2))
+ self.radii = np.zeros(self.n_circles)
+
+ self._initialize_centers()
+
+ def _initialize_centers(self):
+ """
+ Initializes the center positions of the circles based on a grid pattern
+ and a specific placement for the extra circle.
+ """
+ grid_size = self.config["grid_size"]
+ spacing = 1.0 / grid_size
+
+ k = 0
+ for i in range(grid_size):
+ for j in range(grid_size):
+ # Fill up to N-1 circles in the grid
+ if k < self.n_circles - 1:
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break # All grid spots for N-1 circles are filled
+ if k == self.n_circles - 1:
+ break # All grid spots for N-1 circles are filled
+
+ # Place the last circle at the specified interstitial position
+ # For n=26, grid_size=5 means 25 circles are placed, so the 26th is centers[25]
+ self.centers[self.n_circles - 1] = self.config["extra_circle_initial_pos"]
+
+ def _compute_max_radii(self):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Returns:
+ np.array: An array of radii for each circle.
+ """
+ radii = np.ones(self.n_circles)
+
+ # First, limit by distance to square borders
+ for i in range(self.n_circles):
+ x, y = self.centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(self.config["radii_iterations"]):
+ updated_in_pass = False
+ for i in range(self.n_circles):
+ for j in range(i + 1, self.n_circles):
+ dist = np.linalg.norm(self.centers[i] - self.centers[j])
+
+ # Only adjust if there's an overlap and circles are not at identical positions
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+ def _calculate_forces(self, current_radii):
+ """
+ Calculates repulsion forces between circles and from walls.
+ Uses inflated radii for a "growth pressure" effect.
+
+ Args:
+ current_radii (np.array): The current radii of the circles.
+
+ Returns:
+ np.array: An array of (dx, dy) forces for each circle.
+ """
+ forces = np.zeros((self.n_circles, 2))
+ growth_pressure = self.config["growth_pressure"]
+ wall_repulsion_strength = self.config["wall_repulsion_strength"]
+
+ inflated_radii = current_radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces (vectorized)
+ # Calculate differences between all pairs of centers (N, N, 2)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+
+ # Calculate squared distances (N, N)
+ dist_sq = np.sum(diffs**2, axis=-1)
+
+ # Calculate distances (N, N), add epsilon for numerical stability
+ dist = np.sqrt(dist_sq + 1e-9)
+
+ # Calculate sum of inflated radii for all pairs (N, N)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ # Calculate overlap for all pairs (N, N)
+ overlap_matrix = sum_inflated_radii - dist
+
+ # Only consider positive overlap (repulsion) and prevent self-repulsion
+ np.fill_diagonal(overlap_matrix, 0)
+ overlap_matrix[overlap_matrix < 0] = 0
+
+ # Calculate unit vectors for force direction (N, N, 2)
+ # Use np.errstate to suppress division warnings for very small distances
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vecs = diffs / dist[:, :, np.newaxis]
+ # Set force direction to zero if distance is negligible to prevent NaN/Inf
+ unit_vecs[dist < 1e-9] = 0
+
+ # Force magnitudes are proportional to overlap (N, N)
+ force_magnitudes = overlap_matrix
+
+ # Total forces on each circle from all others (N, 2)
+ # Sum forces applied BY other circles (axis=1) onto the current circle
+ forces += np.sum(unit_vecs * force_magnitudes[:, :, np.newaxis], axis=1)
+
+
+ # b. Wall repulsion forces (vectorized)
+ x_centers, y_centers = self.centers[:, 0], self.centers[:, 1]
+ r_inflated = inflated_radii
+
+ # Overlap with all four walls for all circles
+ overlap_left = r_inflated - x_centers
+ overlap_right = x_centers + r_inflated - 1.0
+ overlap_bottom = r_inflated - y_centers
+ overlap_top = y_centers + r_inflated - 1.0
+
+ # Apply forces only for positive overlaps
+ forces[:, 0] += wall_repulsion_strength * np.where(overlap_left > 0, overlap_left, 0)
+ forces[:, 0] -= wall_repulsion_strength * np.where(overlap_right > 0, overlap_right, 0)
+ forces[:, 1] += wall_repulsion_strength * np.where(overlap_bottom > 0, overlap_bottom, 0)
+ forces[:, 1] -= wall_repulsion_strength * np.where(overlap_top > 0, overlap_top, 0)
+
+ return forces
+
+ def _update_positions(self, forces, current_lr):
+ """
+ Updates circle positions based on calculated forces and learning rate,
+ clipping them to stay within the unit square.
+
+ Args:
+ forces (np.array): The forces acting on each circle.
+ current_lr (float): The current learning rate for position updates.
+ """
+ self.centers += forces * current_lr
+ self.centers = np.clip(self.centers, 0.0, 1.0) # Ensure centers stay within [0,1]
+
+ def simulate(self):
+ """
+ Runs the full physical simulation to optimize circle positions.
+ """
+ iterations = self.config["iterations"]
+ learning_rate = self.config["learning_rate"]
+
+ for k_iter in range(iterations):
+ current_radii = self._compute_max_radii()
+ forces = self._calculate_forces(current_radii)
+
+ # Annealing learning rate: decreases over time to help the system settle
+ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+ self._update_positions(forces, current_lr)
+
+ # Final calculation of radii after stabilization to get the best fit
+ self.radii = self._compute_max_radii()
+
+ def get_results(self):
+ """
+ Returns the optimized circle centers and radii.
+
+ Returns:
+ tuple: A tuple containing:
+ - np.array: The final optimized centers.
+ - np.array: The final radii corresponding to the centers.
+ """
+ return self.centers, self.radii
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using the CirclePacker class.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # Define hyperparameters for the simulation
+ config = {
+ "n_circles": n,
+ "iterations": 500, # Number of simulation steps
+ "learning_rate": 0.01, # Controls how much positions change per step
+ "wall_repulsion_strength": 0.5, # Strength of forces from boundaries
+ "growth_pressure": 1.005, # Factor to inflate radii for repulsion, encouraging expansion
+ "grid_size": 5, # For the initial 5x5 grid arrangement
+ "extra_circle_initial_pos": [0.2, 0.2], # Optimized initial position for the 26th circle
+ "radii_iterations": 250, # Iterations for the `_compute_max_radii` function
+ }
+
+ # Instantiate and run the CirclePacker simulation
+ packer = CirclePacker(n, config)
+ packer.simulate()
+
+ return packer.get_results()
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa667478dee645e6313153e219f5465b2e4e975a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/original.py
@@ -0,0 +1,164 @@
+# EVOLVE-BLOCK-START
+"""
+Improved physical simulation packing for n=26 circles.
+This algorithm starts with a more symmetric and promising grid-based
+configuration and uses an enhanced force model with "growth pressure"
+to actively seek out more optimal arrangements.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by starting with an interstitial
+ grid layout and then simulating physical forces to find an optimal
+ arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # More iterations to allow the system to settle into a fine-tuned state.
+ iterations = 500
+ # A smaller learning rate is used for careful refinement of a good initial guess.
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ # A growth factor to create a "pressure" for circles to expand,
+ # generating repulsive forces even when they are just touching.
+ growth_pressure = 1.005
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, a proven strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the center of a grid cell. This is a
+ # more balanced starting point than a corner, as it doesn't
+ # immediately crowd any single circle. It mimics the known optimal
+ # structure for N=26.
+ centers[25] = [0.2, 0.2]
+
+ # No random noise is added. We want to deterministically refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Use inflated radii to calculate overlap, creating a growth pressure
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap = inflated_radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = inflated_radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..532a184ac05b691e218af70a296a60241c8442c8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": false,
+ "error": "name 'np' is not defined"
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..2d427ec303cb376fd56b325f02e35d1a2ec792dd
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/job_log.out
@@ -0,0 +1,14 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results
+Evaluation error: name 'np' is not defined
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/metrics.json
+Evaluation or Validation failed: name 'np' is not defined
+Metrics:
+ combined_score: 0.0
+ execution_time_mean: 0.0
+ execution_time_std: 0.0
+ num_successful_runs: 0
+ num_valid_runs: 0
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..fec2021a6a036637c9d3049ae9778c77cef78421
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/results/metrics.json
@@ -0,0 +1,9 @@
+{
+ "combined_score": 0.0,
+ "execution_time_mean": 0.0,
+ "execution_time_std": 0.0,
+ "num_successful_runs": 0,
+ "num_valid_runs": 0,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c5fae55576f9fb8128dede0b1679be99faeabd5e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_19/rewrite.txt
@@ -0,0 +1,244 @@
+import numpy as np
+
+# EVOLVE-BLOCK-START
+class CirclePacker:
+ """
+ Encapsulates the state and logic for packing N circles in a unit square
+ using a force-directed simulation.
+
+ Attributes:
+ n_circles (int): The number of circles to pack.
+ config (dict): A dictionary containing hyperparameters for the simulation.
+ centers (np.array): Current (x, y) coordinates of circle centers.
+ radii (np.array): Current radii of circles.
+ """
+
+ def __init__(self, n_circles, config):
+ """
+ Initializes the CirclePacker with the number of circles and configuration.
+
+ Args:
+ n_circles (int): The number of circles.
+ config (dict): Hyperparameters for the simulation.
+ """
+ self.n_circles = n_circles
+ self.config = config
+
+ self.centers = np.zeros((self.n_circles, 2))
+ self.radii = np.zeros(self.n_circles)
+
+ self._initialize_centers()
+
+ def _initialize_centers(self):
+ """
+ Initializes the center positions of the circles based on a grid pattern
+ and a specific placement for the extra circle.
+ """
+ grid_size = self.config["grid_size"]
+ spacing = 1.0 / grid_size
+
+ k = 0
+ for i in range(grid_size):
+ for j in range(grid_size):
+ # Fill up to N-1 circles in the grid
+ if k < self.n_circles - 1:
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break # All grid spots for N-1 circles are filled
+ if k == self.n_circles - 1:
+ break # All grid spots for N-1 circles are filled
+
+ # Place the last circle at the specified interstitial position
+ # For n=26, grid_size=5 means 25 circles are placed, so the 26th is centers[25]
+ self.centers[self.n_circles - 1] = self.config["extra_circle_initial_pos"]
+
+ def _compute_max_radii(self):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Returns:
+ np.array: An array of radii for each circle.
+ """
+ radii = np.ones(self.n_circles)
+
+ # First, limit by distance to square borders
+ for i in range(self.n_circles):
+ x, y = self.centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(self.config["radii_iterations"]):
+ updated_in_pass = False
+ for i in range(self.n_circles):
+ for j in range(i + 1, self.n_circles):
+ dist = np.linalg.norm(self.centers[i] - self.centers[j])
+
+ # Only adjust if there's an overlap and circles are not at identical positions
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+ def _calculate_forces(self, current_radii):
+ """
+ Calculates repulsion forces between circles and from walls.
+ Uses inflated radii for a "growth pressure" effect.
+
+ Args:
+ current_radii (np.array): The current radii of the circles.
+
+ Returns:
+ np.array: An array of (dx, dy) forces for each circle.
+ """
+ forces = np.zeros((self.n_circles, 2))
+ growth_pressure = self.config["growth_pressure"]
+ wall_repulsion_strength = self.config["wall_repulsion_strength"]
+
+ inflated_radii = current_radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces (vectorized)
+ # Calculate differences between all pairs of centers (N, N, 2)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+
+ # Calculate squared distances (N, N)
+ dist_sq = np.sum(diffs**2, axis=-1)
+
+ # Calculate distances (N, N), add epsilon for numerical stability
+ dist = np.sqrt(dist_sq + 1e-9)
+
+ # Calculate sum of inflated radii for all pairs (N, N)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ # Calculate overlap for all pairs (N, N)
+ overlap_matrix = sum_inflated_radii - dist
+
+ # Only consider positive overlap (repulsion) and prevent self-repulsion
+ np.fill_diagonal(overlap_matrix, 0)
+ overlap_matrix[overlap_matrix < 0] = 0
+
+ # Calculate unit vectors for force direction (N, N, 2)
+ # Use np.errstate to suppress division warnings for very small distances
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vecs = diffs / dist[:, :, np.newaxis]
+ # Set force direction to zero if distance is negligible to prevent NaN/Inf
+ unit_vecs[dist < 1e-9] = 0
+
+ # Force magnitudes are proportional to overlap (N, N)
+ force_magnitudes = overlap_matrix
+
+ # Total forces on each circle from all others (N, 2)
+ # Sum forces applied BY other circles (axis=1) onto the current circle
+ forces += np.sum(unit_vecs * force_magnitudes[:, :, np.newaxis], axis=1)
+
+
+ # b. Wall repulsion forces (vectorized)
+ x_centers, y_centers = self.centers[:, 0], self.centers[:, 1]
+ r_inflated = inflated_radii
+
+ # Overlap with all four walls for all circles
+ overlap_left = r_inflated - x_centers
+ overlap_right = x_centers + r_inflated - 1.0
+ overlap_bottom = r_inflated - y_centers
+ overlap_top = y_centers + r_inflated - 1.0
+
+ # Apply forces only for positive overlaps
+ forces[:, 0] += wall_repulsion_strength * np.where(overlap_left > 0, overlap_left, 0)
+ forces[:, 0] -= wall_repulsion_strength * np.where(overlap_right > 0, overlap_right, 0)
+ forces[:, 1] += wall_repulsion_strength * np.where(overlap_bottom > 0, overlap_bottom, 0)
+ forces[:, 1] -= wall_repulsion_strength * np.where(overlap_top > 0, overlap_top, 0)
+
+ return forces
+
+ def _update_positions(self, forces, current_lr):
+ """
+ Updates circle positions based on calculated forces and learning rate,
+ clipping them to stay within the unit square.
+
+ Args:
+ forces (np.array): The forces acting on each circle.
+ current_lr (float): The current learning rate for position updates.
+ """
+ self.centers += forces * current_lr
+ self.centers = np.clip(self.centers, 0.0, 1.0) # Ensure centers stay within [0,1]
+
+ def simulate(self):
+ """
+ Runs the full physical simulation to optimize circle positions.
+ """
+ iterations = self.config["iterations"]
+ learning_rate = self.config["learning_rate"]
+
+ for k_iter in range(iterations):
+ current_radii = self._compute_max_radii()
+ forces = self._calculate_forces(current_radii)
+
+ # Annealing learning rate: decreases over time to help the system settle
+ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+ self._update_positions(forces, current_lr)
+
+ # Final calculation of radii after stabilization to get the best fit
+ self.radii = self._compute_max_radii()
+
+ def get_results(self):
+ """
+ Returns the optimized circle centers and radii.
+
+ Returns:
+ tuple: A tuple containing:
+ - np.array: The final optimized centers.
+ - np.array: The final radii corresponding to the centers.
+ """
+ return self.centers, self.radii
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using the CirclePacker class.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # Define hyperparameters for the simulation
+ config = {
+ "n_circles": n,
+ "iterations": 500, # Number of simulation steps
+ "learning_rate": 0.01, # Controls how much positions change per step
+ "wall_repulsion_strength": 0.5, # Strength of forces from boundaries
+ "growth_pressure": 1.005, # Factor to inflate radii for repulsion, encouraging expansion
+ "grid_size": 5, # For the initial 5x5 grid arrangement
+ "extra_circle_initial_pos": [0.2, 0.2], # Optimized initial position for the 26th circle
+ "radii_iterations": 250, # Iterations for the `_compute_max_radii` function
+ }
+
+ # Instantiate and run the CirclePacker simulation
+ packer = CirclePacker(n, config)
+ packer.simulate()
+
+ return packer.get_results()
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3e1d998066d9e0408ef1fdb40b4d65b052ebdea3
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..4eda91fdacc0f208cdd6968192a3166ad3d469a7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/edit.diff
@@ -0,0 +1,178 @@
+--- a/original.py
++++ b/original.py
+@@ -1,94 +1,124 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
++"""
++Force-directed relaxation packing for n=26 circles.
++Starts with a jittered grid and refines positions using simulated forces,
++then computes radii using a convergent iterative process.
++"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+- Construct a specific arrangement of 26 circles in a unit square
+- that attempts to maximize the sum of their radii.
++ Constructs an arrangement of 26 circles using a force-directed
++ relaxation method to maximize the sum of radii.
+
+ Returns:
+- Tuple of (centers, radii, sum_of_radii)
++ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+- sum_of_radii: Sum of all radii
+ """
+- # Initialize arrays for 26 circles
+ n = 26
++ # Use a fixed seed for reproducibility of the random initialization.
++ np.random.seed(1337)
++
++ # 1. Initial Placement: Start with a perturbed 5x5 grid + 1 extra circle.
++ # This provides a good structured start, better than pure random.
+ centers = np.zeros((n, 2))
++ grid_size = 5
++ # Create a 5x5 grid
++ x = np.linspace(0.1, 0.9, grid_size)
++ y = np.linspace(0.1, 0.9, grid_size)
++ xv, yv = np.meshgrid(x, y)
++ centers[:25, :] = np.vstack([xv.ravel(), yv.ravel()]).T
+
+- # Place circles in a structured pattern
+- # This is a simple pattern - evolution will improve this
++ # Place the 26th circle in one of the initial gaps of the grid.
++ centers[25, :] = [0.2, 0.2]
+
+- # First, place a large circle in the center
+- centers[0] = [0.5, 0.5]
++ # Add random jitter to break perfect symmetry and allow for new configurations.
++ centers += np.random.uniform(-0.05, 0.05, size=centers.shape)
++ centers = np.clip(centers, 0.01, 0.99) # Ensure they start safely inside.
+
+- # Place 8 circles around it in a ring
+- for i in range(8):
+- angle = 2 * np.pi * i / 8
+- centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
++ # 2. Iterative Position Refinement (Force Simulation)
++ n_iter = 250
++ step_size = 1e-4
++ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+- # Place 16 more circles in an outer ring
+- for i in range(16):
+- angle = 2 * np.pi * i / 16
+- centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
++ for _ in range(n_iter):
++ # Vectorized calculation of repulsion forces between all pairs of circles
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dist_sq = np.sum(diffs**2, axis=-1)
++ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
++ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
++ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
++ force_repel = np.sum(force_repel_matrix, axis=1)
+
+- # Additional positioning adjustment to make sure all circles
+- # are inside the square and don't overlap
+- # Clip to ensure everything is inside the unit square
+- centers = np.clip(centers, 0.01, 0.99)
++ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
++ # This creates a soft potential well keeping circles inside the square.
++ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
++ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+- # Compute maximum valid radii for this configuration
++ # Combine forces and update positions
++ total_force = force_repel + wall_strength * force_wall
++ centers += step_size * total_force
++
++ # Enforce hard boundaries
++ centers = np.clip(centers, 0.0, 1.0)
++
++ # 3. Compute optimal radii for the final configuration using a convergent method.
+ radii = compute_max_radii(centers)
++
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
++ Compute the maximum possible radii for the given centers using an
++ iterative relaxation method until convergence. This is more robust
++ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+- radii = np.ones(n)
++
++ # Initialize radii based on the distance to the four walls.
++ radii = np.min([
++ centers[:, 0],
++ centers[:, 1],
++ 1 - centers[:, 0],
++ 1 - centers[:, 1]
++ ], axis=0)
+
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
++ # Iteratively shrink any overlapping circles until the layout is stable.
++ max_iter = 500 # Safety break for the convergence loop
++ for _ in range(max_iter):
++ changed_in_iter = False
++ # Check every pair of circles for overlap
++ for i in range(n):
++ for j in range(i + 1, n):
++ dist = np.linalg.norm(centers[i] - centers[j])
++ sum_r = radii[i] + radii[j]
+
+- # Then, limit by distance to other circles
+- # Each pair of circles with centers at distance d can have
+- # sum of radii at most d to avoid overlap
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+-
+- # If current radii would cause overlap
+- if radii[i] + radii[j] > dist:
+- # Scale both radii proportionally
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
++ if sum_r > dist:
++ # Overlap detected. Shrink radii proportionally to their size.
++ overlap = sum_r - dist
++ if sum_r > 1e-9: # Avoid division by zero
++ radii[i] -= overlap * (radii[i] / sum_r)
++ radii[j] -= overlap * (radii[j] / sum_r)
++ changed_in_iter = True
++
++ if not changed_in_iter:
++ # If a full pass completes with no changes, the radii have converged.
++ break
++
+ return radii
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..38d5140fb64ee8d3ffc99eac96a21fc551b36b66
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/main.py
@@ -0,0 +1,124 @@
+# EVOLVE-BLOCK-START
+"""
+Force-directed relaxation packing for n=26 circles.
+Starts with a jittered grid and refines positions using simulated forces,
+then computes radii using a convergent iterative process.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility of the random initialization.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with a perturbed 5x5 grid + 1 extra circle.
+ # This provides a good structured start, better than pure random.
+ centers = np.zeros((n, 2))
+ grid_size = 5
+ # Create a 5x5 grid
+ x = np.linspace(0.1, 0.9, grid_size)
+ y = np.linspace(0.1, 0.9, grid_size)
+ xv, yv = np.meshgrid(x, y)
+ centers[:25, :] = np.vstack([xv.ravel(), yv.ravel()]).T
+
+ # Place the 26th circle in one of the initial gaps of the grid.
+ centers[25, :] = [0.2, 0.2]
+
+ # Add random jitter to break perfect symmetry and allow for new configurations.
+ centers += np.random.uniform(-0.05, 0.05, size=centers.shape)
+ centers = np.clip(centers, 0.01, 0.99) # Ensure they start safely inside.
+
+ # 2. Iterative Position Refinement (Force Simulation)
+ n_iter = 250
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using a convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..9306383d5dbb8671983adb69c529a06d51537b62
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/original.py
@@ -0,0 +1,94 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place circles in a structured pattern
+ # This is a simple pattern - evolution will improve this
+
+ # First, place a large circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles around it in a ring
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
+
+ # Place 16 more circles in an outer ring
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
+
+ # Additional positioning adjustment to make sure all circles
+ # are inside the square and don't overlap
+ # Clip to ensure everything is inside the unit square
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..7a83d9a20a86546bb6732acb13d8f05ad9cc48ed
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results
+Run 1/1 completed in 0.02 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.147629584668997
+ public: {'centers_str': ' centers[0] = (0.0144, 0.0197)\n centers[1] = (0.2424, 0.0227)\n centers[2] = (0.5099, 0.0184)\n centers[3] = (0.8102, 0.0217)\n centers[4] = (0.9812, 0.0140)\n centers[5] = (0.0192, 0.1642)\n centers[6] = (0.0888, 0.2789)\n centers[7] = (0.5464, 0.1309)\n centers[8] = (0.9659, 0.0786)\n centers[9] = (0.9776, 0.2169)\n centers[10] = (0.0223, 0.4845)\n centers[11] = (0.0619, 0.6286)\n centers[12] = (0.5364, 0.4837)\n centers[13] = (0.9383, 0.4376)\n centers[14] = (0.9772, 0.5848)\n centers[15] = (0.0186, 0.8678)\n centers[16] = (0.1935, 0.8996)\n centers[17] = (0.5659, 0.8757)\n centers[18] = (0.9185, 0.9757)\n centers[19] = (0.9776, 0.8095)\n centers[20] = (0.0166, 0.9822)\n centers[21] = (0.1193, 0.9834)\n centers[22] = (0.4431, 0.9791)\n centers[23] = (0.7727, 0.9771)\n centers[24] = (0.9860, 0.9788)\n centers[25] = (0.0856, 0.0197)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.147629584668997}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/packing_viz.png
+ execution_time_mean: 0.022726561408489943
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..d558acbe46e4d6cdc460cba41de6a680555f998d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.147629584668997,
+ "public": {
+ "centers_str": " centers[0] = (0.0144, 0.0197)\n centers[1] = (0.2424, 0.0227)\n centers[2] = (0.5099, 0.0184)\n centers[3] = (0.8102, 0.0217)\n centers[4] = (0.9812, 0.0140)\n centers[5] = (0.0192, 0.1642)\n centers[6] = (0.0888, 0.2789)\n centers[7] = (0.5464, 0.1309)\n centers[8] = (0.9659, 0.0786)\n centers[9] = (0.9776, 0.2169)\n centers[10] = (0.0223, 0.4845)\n centers[11] = (0.0619, 0.6286)\n centers[12] = (0.5364, 0.4837)\n centers[13] = (0.9383, 0.4376)\n centers[14] = (0.9772, 0.5848)\n centers[15] = (0.0186, 0.8678)\n centers[16] = (0.1935, 0.8996)\n centers[17] = (0.5659, 0.8757)\n centers[18] = (0.9185, 0.9757)\n centers[19] = (0.9776, 0.8095)\n centers[20] = (0.0166, 0.9822)\n centers[21] = (0.1193, 0.9834)\n centers[22] = (0.4431, 0.9791)\n centers[23] = (0.7727, 0.9771)\n centers[24] = (0.9860, 0.9788)\n centers[25] = (0.0856, 0.0197)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.147629584668997
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/results/packing_viz.png",
+ "execution_time_mean": 0.022726561408489943,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..59b8f5b8842317c02cae8588824735d4c0f24cc8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_2/rewrite.txt
@@ -0,0 +1,115 @@
+# EVOLVE-BLOCK-START
+"""
+Force-directed relaxation packing for n=26 circles.
+Starts with a jittered grid and refines positions using simulated forces,
+then computes radii using a convergent iterative process.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility of the random initialization.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with a perturbed 5x5 grid + 1 extra circle.
+ # This provides a good structured start, better than pure random.
+ centers = np.zeros((n, 2))
+ grid_size = 5
+ # Create a 5x5 grid
+ x = np.linspace(0.1, 0.9, grid_size)
+ y = np.linspace(0.1, 0.9, grid_size)
+ xv, yv = np.meshgrid(x, y)
+ centers[:25, :] = np.vstack([xv.ravel(), yv.ravel()]).T
+
+ # Place the 26th circle in one of the initial gaps of the grid.
+ centers[25, :] = [0.2, 0.2]
+
+ # Add random jitter to break perfect symmetry and allow for new configurations.
+ centers += np.random.uniform(-0.05, 0.05, size=centers.shape)
+ centers = np.clip(centers, 0.01, 0.99) # Ensure they start safely inside.
+
+ # 2. Iterative Position Refinement (Force Simulation)
+ n_iter = 250
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using a convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4cb21bdc42e5041dd43316420497e3eef44602e8
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..425d4a6ebe045793f4dfb3a8437b3471eb87781c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/edit.diff
@@ -0,0 +1,213 @@
+--- a/original.py
++++ b/original.py
+@@ -1,191 +1,200 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ Improved by combining insights from high-scoring prior programs:
+ - Optimized initial placement of the 26th circle.
+ - Reduced initial perturbation noise.
+ - Tuned learning rate for fine-grained adjustments.
+ - Increased iterations for radius calculation convergence.
+ - Robust handling of very close circle centers in radius calculation.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- iterations = 250 # Number of simulation steps (kept from current)
+- learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+- wall_repulsion_strength = 0.5 # Kept from current
++ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
++ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
++ wall_repulsion_strength = 0.5
++ growth_pressure = 1.005 # Reintroduced from 1.92 program for denser packing
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+- [0.05, 0.05], # Original position
++ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
++ [0.2, 0.2], # Position that yielded 1.92 in a prior program
++ [0.2, 0.8], # Symmetric to [0.2, 0.2]
++ [0.8, 0.2], # Symmetric to [0.2, 0.2]
++ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
++ # Create "inflated" radii to generate repulsive forces even when just touching
++ inflated_radii = radii * growth_pressure
++
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+- overlap = radii[i] + radii[j] - dist
++ # Use inflated radii for overlap calculation
++ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+- # b. Wall repulsion forces
++ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+- overlap_left = radii[i] - centers_for_sim[i, 0]
++ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+- overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
++ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+- overlap_bottom = radii[i] - centers_for_sim[i, 1]
++ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+- overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
++ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # Return the best configuration found across all trials
+ final_centers = best_final_centers
+ final_radii = best_final_radii
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e78a43f372d7dd124e17e2c41d226d246b584f8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/main.py
@@ -0,0 +1,200 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+ growth_pressure = 1.005 # Reintroduced from 1.92 program for denser packing
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Use inflated radii for overlap calculation
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # Return the best configuration found across all trials
+ final_centers = best_final_centers
+ final_radii = best_final_radii
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae84d715fe232fbd4c97402fede1c9092e3d5047
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/original.py
@@ -0,0 +1,191 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250 # Number of simulation steps (kept from current)
+ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+ wall_repulsion_strength = 0.5 # Kept from current
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # Return the best configuration found across all trials
+ final_centers = best_final_centers
+ final_radii = best_final_radii
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..029a9d5d1baf435af2a787ba251ecac716e230ac
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results
+Run 1/1 completed in 13.38 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.923173132299769
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1002, 0.8998)\n centers[5] = (0.3008, 0.0992)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.6983, 0.6998)\n centers[19] = (0.7000, 0.9002)\n centers[20] = (0.8999, 0.0994)\n centers[21] = (0.8996, 0.2998)\n centers[22] = (0.8996, 0.5010)\n centers[23] = (0.9017, 0.7002)\n centers[24] = (0.8996, 0.8996)\n centers[25] = (0.1992, 0.2008)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.923173132299769}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/packing_viz.png
+ execution_time_mean: 13.380122774746269
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..e789c359434f975b0dc59c38730b89dda73c450b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.923173132299769,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1002, 0.8998)\n centers[5] = (0.3008, 0.0992)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.6983, 0.6998)\n centers[19] = (0.7000, 0.9002)\n centers[20] = (0.8999, 0.0994)\n centers[21] = (0.8996, 0.2998)\n centers[22] = (0.8996, 0.5010)\n centers[23] = (0.9017, 0.7002)\n centers[24] = (0.8996, 0.8996)\n centers[25] = (0.1992, 0.2008)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.923173132299769
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/results/packing_viz.png",
+ "execution_time_mean": 13.380122774746269,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a973161db59549a8380b826fe456f530c18cebbe
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_20/search_replace.txt
@@ -0,0 +1,188 @@
+
+
+This edit integrates the `growth_pressure` mechanism, which proved effective in a prior program (1.92 score), into the current simulation. It also updates the `iterations` count and `learning_rate` to match the configuration that achieved the higher score. The `growth_pressure` makes circles "want" to expand, thus generating repulsive forces even when they are just touching, leading to a tighter packing. The force calculation for both circle-to-circle and wall repulsions is modified to use these 'inflated' radii.
+
+
+<<<<<<< SEARCH
+ # --- Hyperparameters for the simulation ---
+ iterations = 250 # Number of simulation steps (kept from current)
+ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+ wall_repulsion_strength = 0.5 # Kept from current
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+=======
+ # --- Hyperparameters for the simulation ---
+ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+ growth_pressure = 1.005 # Reintroduced from 1.92 program for denser packing
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Use inflated radii for overlap calculation
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..2dbec1eb0ea8f119389e866aa2150219913322fa
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/edit.diff
@@ -0,0 +1,194 @@
+--- a/original.py
++++ b/original.py
+@@ -1,139 +1,186 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers
+ current_centers[25] = extra_pos
+
+ current_radii = compute_max_radii(current_centers)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+- best_centers = current_centers
+- best_radii = current_radii
++ best_centers = current_centers.copy() # Use .copy() to prevent modifying shared reference
++ # Note: best_radii is not directly used for the final output, as it will be recomputed.
++ # We only need best_centers from this loop.
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ centers = np.zeros((n, 2))
+ centers[:25] = base_centers
+ centers[25] = [0.5, 0.5] # Default to center for 26th circle
+- radii = compute_max_radii(centers)
+ else:
+ centers = best_centers
+- radii = best_radii
+
+- return centers, radii
++ # 3. Apply force-directed refinement to the best initial configuration
++ # Use conservative parameters to gently fine-tune the positions.
++ refined_centers = refine_centers_force_directed(centers, num_iterations=100, step=1e-5, wall_str=0.1)
++
++ # 4. Compute optimal radii for the final refined configuration
++ final_radii = compute_max_radii(refined_centers)
++
++ return refined_centers, final_radii
++
++
++def refine_centers_force_directed(centers, num_iterations=100, step=1e-5, wall_str=0.1):
++ """
++ Refines circle centers using a force-directed simulation.
++
++ Args:
++ centers: np.array of shape (n, 2)
++ num_iterations: number of simulation steps
++ step: learning rate/step size for center updates
++ wall_str: relative strength of wall repulsion force
++
++ Returns:
++ Refined centers
++ """
++ n = centers.shape[0]
++ for _ in range(num_iterations):
++ # Calculate differences between all pairs of centers
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dist_sq = np.sum(diffs**2, axis=-1)
++
++ # Avoid self-repulsion and division by zero; cap minimum distance for force calculation
++ np.fill_diagonal(dist_sq, 1.0)
++ dist_sq = np.maximum(dist_sq, 1e-6) # Ensure a minimum non-zero distance
++
++ # Force repulsion (F ~ 1/r^2)
++ # The force is proportional to diffs / dist_sq
++ force_repel = np.sum(diffs * (1.0 / dist_sq)[:, :, np.newaxis], axis=1)
++
++ # Wall repulsion: F_wall is proportional to 1/d - 1/(1-d)
++ # This creates a strong repulsion near the boundaries.
++ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6) # Avoid division by zero near boundaries
++ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
++
++ # Combine forces and update positions
++ total_force = force_repel + wall_str * force_wall
++ centers += step * total_force
++
++ # Enforce hard boundaries by clipping positions to [0, 1]
++ centers = np.clip(centers, 0.0, 1.0)
++ return centers
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..91a410cc93f7e5d6160b22109a017df061001540
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/main.py
@@ -0,0 +1,186 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers
+ current_centers[25] = extra_pos
+
+ current_radii = compute_max_radii(current_centers)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = current_centers.copy() # Use .copy() to prevent modifying shared reference
+ # Note: best_radii is not directly used for the final output, as it will be recomputed.
+ # We only need best_centers from this loop.
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ centers = np.zeros((n, 2))
+ centers[:25] = base_centers
+ centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ else:
+ centers = best_centers
+
+ # 3. Apply force-directed refinement to the best initial configuration
+ # Use conservative parameters to gently fine-tune the positions.
+ refined_centers = refine_centers_force_directed(centers, num_iterations=100, step=1e-5, wall_str=0.1)
+
+ # 4. Compute optimal radii for the final refined configuration
+ final_radii = compute_max_radii(refined_centers)
+
+ return refined_centers, final_radii
+
+
+def refine_centers_force_directed(centers, num_iterations=100, step=1e-5, wall_str=0.1):
+ """
+ Refines circle centers using a force-directed simulation.
+
+ Args:
+ centers: np.array of shape (n, 2)
+ num_iterations: number of simulation steps
+ step: learning rate/step size for center updates
+ wall_str: relative strength of wall repulsion force
+
+ Returns:
+ Refined centers
+ """
+ n = centers.shape[0]
+ for _ in range(num_iterations):
+ # Calculate differences between all pairs of centers
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+
+ # Avoid self-repulsion and division by zero; cap minimum distance for force calculation
+ np.fill_diagonal(dist_sq, 1.0)
+ dist_sq = np.maximum(dist_sq, 1e-6) # Ensure a minimum non-zero distance
+
+ # Force repulsion (F ~ 1/r^2)
+ # The force is proportional to diffs / dist_sq
+ force_repel = np.sum(diffs * (1.0 / dist_sq)[:, :, np.newaxis], axis=1)
+
+ # Wall repulsion: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a strong repulsion near the boundaries.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6) # Avoid division by zero near boundaries
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_str * force_wall
+ centers += step * total_force
+
+ # Enforce hard boundaries by clipping positions to [0, 1]
+ centers = np.clip(centers, 0.0, 1.0)
+ return centers
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c62f05af02e898eb2fbc02f6d58c10848f6776b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/original.py
@@ -0,0 +1,139 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers
+ current_centers[25] = extra_pos
+
+ current_radii = compute_max_radii(current_centers)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = current_centers
+ best_radii = current_radii
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ centers = np.zeros((n, 2))
+ centers[:25] = base_centers
+ centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ radii = compute_max_radii(centers)
+ else:
+ centers = best_centers
+ radii = best_radii
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0787aaf65e5b577e2eea79b8c178bb211e1fffba
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_21/search_replace.txt
@@ -0,0 +1,110 @@
+
+A new function `refine_centers_force_directed` is added. This function applies a gentle force-directed simulation to a set of circle centers. It includes both repulsion between circles (using an inverse square law) and repulsion from the boundaries of the unit square. This allows for fine-tuning the positions of circles, aiming to release any subtle strains in the initial packing and potentially allowing for slightly larger radii, without drastically altering a good initial configuration. The parameters `num_iterations`, `step`, and `wall_str` are chosen to be conservative, ensuring a gentle "jiggle" rather than a full re-arrangement.
+
+
+
+<<<<<<< SEARCH
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+=======
+ return centers, radii
+
+
+def refine_centers_force_directed(centers, num_iterations=100, step=1e-5, wall_str=0.1):
+ """
+ Refines circle centers using a force-directed simulation.
+
+ Args:
+ centers: np.array of shape (n, 2)
+ num_iterations: number of simulation steps
+ step: learning rate/step size for center updates
+ wall_str: relative strength of wall repulsion force
+
+ Returns:
+ Refined centers
+ """
+ n = centers.shape[0]
+ for _ in range(num_iterations):
+ # Calculate differences between all pairs of centers
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+
+ # Avoid self-repulsion and division by zero; cap minimum distance for force calculation
+ np.fill_diagonal(dist_sq, 1.0)
+ dist_sq = np.maximum(dist_sq, 1e-6) # Ensure a minimum non-zero distance
+
+ # Force repulsion (F ~ 1/r^2)
+ # The force is proportional to diffs / dist_sq
+ force_repel = np.sum(diffs * (1.0 / dist_sq)[:, :, np.newaxis], axis=1)
+
+ # Wall repulsion: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a strong repulsion near the boundaries.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6) # Avoid division by zero near boundaries
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_str * force_wall
+ centers += step * total_force
+
+ # Enforce hard boundaries by clipping positions to [0, 1]
+ centers = np.clip(centers, 0.0, 1.0)
+ return centers
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+>>>>>>> REPLACE
+
+
+
+After finding the best initial static arrangement of circles by iterating through candidate positions for the 26th circle, this edit applies a force-directed refinement step to the `best_centers`. This aims to further optimize the positions by allowing the circles to slightly adjust to minimize internal stresses and maximize available space. Finally, `compute_max_radii` is called again on these refined centers to determine the largest possible radii for the dynamically adjusted configuration. This hybrid approach combines the strength of a structured initial placement with the fine-tuning capabilities of a force simulation.
+
+
+
+<<<<<<< SEARCH
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = current_centers
+ best_radii = current_radii
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ centers = np.zeros((n, 2))
+ centers[:25] = base_centers
+ centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ radii = compute_max_radii(centers)
+ else:
+ centers = best_centers
+ radii = best_radii
+
+ return centers, radii
+=======
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = current_centers.copy() # Use .copy() to prevent modifying shared reference
+ # Note: best_radii is not directly used for the final output, as it will be recomputed.
+ # We only need best_centers from this loop.
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ centers = np.zeros((n, 2))
+ centers[:25] = base_centers
+ centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ else:
+ centers = best_centers
+
+ # 3. Apply force-directed refinement to the best initial configuration
+ # Use conservative parameters to gently fine-tune the positions.
+ refined_centers = refine_centers_force_directed(centers, num_iterations=100, step=1e-5, wall_str=0.1)
+
+ # 4. Compute optimal radii for the final refined configuration
+ final_radii = compute_max_radii(refined_centers)
+
+ return refined_centers, final_radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..48e16ade115600b479f60d9632e924cec8e28ed0
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..a739340501574f719582837b4bc073a7030ee838
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/edit.diff
@@ -0,0 +1,218 @@
+--- a/original.py
++++ b/original.py
+@@ -1,139 +1,201 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
++ # --- Simulation Hyperparameters ---
++ simulation_iterations = 500 # More simulation steps for better convergence
++ initial_learning_rate = 0.05 # Slightly more aggressive initial movement
++ wall_repulsion_strength = 0.5 # Kept from prior working simulations
++
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+- current_centers[:25] = base_centers
+- current_centers[25] = extra_pos
+-
+- current_radii = compute_max_radii(current_centers)
++ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
++ current_centers[25] = np.array(extra_pos)
++
++ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
++ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
++
++ for sim_iter in range(simulation_iterations):
++ # 1. Calculate radii for the current center configuration
++ radii_for_sim = compute_max_radii(centers_for_sim)
++
++ # 2. Calculate forces to push centers to a better configuration
++ forces = np.zeros((n, 2))
++
++ # a. Circle-to-circle repulsion forces
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers_for_sim[i] - centers_for_sim[j]
++ dist = np.linalg.norm(vec)
++
++ if dist < 1e-9: # Centers are too close, effectively identical
++ continue # No force calculation needed, radii_for_sim handles overlap
++
++ overlap = radii_for_sim[i] + radii_for_sim[j] - dist
++
++ if overlap > 0:
++ # Force magnitude proportional to overlap
++ force_magnitude = overlap
++ force_vec = (vec / dist) * force_magnitude
++ forces[i] += force_vec
++ forces[j] -= force_vec # Equal and opposite reaction
++
++ # b. Wall repulsion forces
++ for i in range(n):
++ # Left wall
++ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
++ if overlap_left > 0:
++ forces[i, 0] += wall_repulsion_strength * overlap_left
++ # Right wall
++ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
++ if overlap_right > 0:
++ forces[i, 0] -= wall_repulsion_strength * overlap_right
++ # Bottom wall
++ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
++ if overlap_bottom > 0:
++ forces[i, 1] += wall_repulsion_strength * overlap_bottom
++ # Top wall
++ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
++ if overlap_top > 0:
++ forces[i, 1] -= wall_repulsion_strength * overlap_top
++
++ # 3. Update center positions based on forces
++ # Annealing learning rate: starts high, decreases over time
++ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
++ centers_for_sim += forces * current_lr
++
++ # 4. Enforce boundary constraints - keep centers within [0,1]
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # After simulation, use the refined centers to compute final radii
++ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+- best_centers = current_centers
+- best_radii = current_radii
++ best_centers = centers_for_sim.copy() # Store the optimized centers
++ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+- centers = np.zeros((n, 2))
+- centers[:25] = base_centers
+- centers[25] = [0.5, 0.5] # Default to center for 26th circle
+- radii = compute_max_radii(centers)
++ # If the loop somehow failed to find any valid configuration
++ # This fallback needs to be reconsidered if simulation can lead to all radii being zero
++ # For now, it will use the base 5x5 + center as a safe default
++ final_centers = np.zeros((n, 2))
++ final_centers[:25] = base_centers
++ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
++ final_radii = compute_max_radii(final_centers)
++ return final_centers, final_radii
+ else:
+- centers = best_centers
+- radii = best_radii
+-
+- return centers, radii
++ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..59273f5344ff344d766f71645eebc3282e2764ce
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/main.py
@@ -0,0 +1,201 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # More simulation steps for better convergence
+ initial_learning_rate = 0.05 # Slightly more aggressive initial movement
+ wall_repulsion_strength = 0.5 # Kept from prior working simulations
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current center configuration
+ radii_for_sim = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+ continue # No force calculation needed, radii_for_sim handles overlap
+
+ overlap = radii_for_sim[i] + radii_for_sim[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute final radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # If the loop somehow failed to find any valid configuration
+ # This fallback needs to be reconsidered if simulation can lead to all radii being zero
+ # For now, it will use the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c62f05af02e898eb2fbc02f6d58c10848f6776b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/original.py
@@ -0,0 +1,139 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers
+ current_centers[25] = extra_pos
+
+ current_radii = compute_max_radii(current_centers)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = current_centers
+ best_radii = current_radii
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ centers = np.zeros((n, 2))
+ centers[:25] = base_centers
+ centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ radii = compute_max_radii(centers)
+ else:
+ centers = best_centers
+ radii = best_radii
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..d0c19c0e62f969fb7802cf3e9e304b8d147517ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results
+Run 1/1 completed in 256.91 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9169561144739513
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.9000, 0.1000)\n centers[5] = (0.1000, 0.3000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.5000, 0.7000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.9000, 0.7000)\n centers[20] = (0.1000, 0.9000)\n centers[21] = (0.3000, 0.9000)\n centers[22] = (0.5000, 0.9000)\n centers[23] = (0.7000, 0.9000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.2000, 0.2000)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9169561144739513}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/packing_viz.png
+ execution_time_mean: 256.9138026898727
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..26f5c4ce19d96918aa4f9c9ed800a8db783d01cb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9169561144739513,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.9000, 0.1000)\n centers[5] = (0.1000, 0.3000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.5000, 0.7000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.9000, 0.7000)\n centers[20] = (0.1000, 0.9000)\n centers[21] = (0.3000, 0.9000)\n centers[22] = (0.5000, 0.9000)\n centers[23] = (0.7000, 0.9000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.2000, 0.2000)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9169561144739513
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/results/packing_viz.png",
+ "execution_time_mean": 256.9138026898727,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e473cf72fed4a5ca92fcfccfc63a62fa6e8ff817
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_22/search_replace.txt
@@ -0,0 +1,129 @@
+
+Integrate a physical simulation (force-directed relaxation) phase into the `construct_packing` function. This will allow the initially placed centers to adjust their positions iteratively to minimize overlaps and maximize radii. The simulation will run for each candidate initial placement of the 26th circle, and the best resulting configuration will be selected. This significantly enhances the optimization capability beyond purely static initial placements. The `compute_max_radii` function remains unchanged with its 500 iterations for accurate radius determination at each simulation step.
+
+
+
+<<<<<<< SEARCH
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers
+ current_centers[25] = extra_pos
+
+ current_radii = compute_max_radii(current_centers)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = current_centers
+ best_radii = current_radii
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ centers = np.zeros((n, 2))
+ centers[:25] = base_centers
+ centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ radii = compute_max_radii(centers)
+ else:
+ centers = best_centers
+ radii = best_radii
+
+ return centers, radii
+=======
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # More simulation steps for better convergence
+ initial_learning_rate = 0.05 # Slightly more aggressive initial movement
+ wall_repulsion_strength = 0.5 # Kept from prior working simulations
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current center configuration
+ radii_for_sim = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+ continue # No force calculation needed, radii_for_sim handles overlap
+
+ overlap = radii_for_sim[i] + radii_for_sim[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute final radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # If the loop somehow failed to find any valid configuration
+ # This fallback needs to be reconsidered if simulation can lead to all radii being zero
+ # For now, it will use the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..d3fd53db28db257179b940ead3eba81b50439c83
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/edit.diff
@@ -0,0 +1,149 @@
+--- a/original.py
++++ b/original.py
+@@ -1,104 +1,145 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+- # Compute maximum valid radii for this superior configuration.
++ # Refine the strong initial grid using a force-directed simulation. This allows
++ # the circles to shift from the rigid grid into a more optimal packing.
++ centers = refine_centers_by_force(centers)
++
++ # Compute maximum valid radii for the refined configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
++
++
++def refine_centers_by_force(centers, n_iter=400, learning_rate=2e-5, wall_strength=0.4):
++ """
++ Refines circle centers using a vectorized force-directed simulation.
++ - Repulsion force is proportional to 1/distance.
++ - Wall force creates a potential well to keep circles inside the boundary.
++ """
++ n = centers.shape[0]
++
++ for sim_iter in range(n_iter):
++ # Vectorized calculation of repulsion forces (F ~ 1/r)
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dist_sq = np.sum(diffs**2, axis=-1)
++ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion
++
++ # Add a small epsilon to prevent division by zero for any coincident points
++ dist_sq = np.maximum(dist_sq, 1e-7)
++
++ inv_dist_sq = 1.0 / dist_sq
++ force_repel = np.sum(diffs * inv_dist_sq[:, :, np.newaxis], axis=1)
++
++ # Wall repulsion force to create a soft potential well
++ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
++ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
++
++ # Linearly anneal the learning rate
++ current_lr = learning_rate * (1.0 - (sim_iter / n_iter))
++
++ # Combine forces and update positions
++ total_force = force_repel + wall_strength * force_wall
++ centers += current_lr * total_force
++
++ # Enforce hard boundaries
++ centers = np.clip(centers, 0.0, 1.0)
++
++ return centers
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # Increased iterations to ensure full convergence for a dense packing.
+ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1b640d8bb7f6d2db9dc00881f2672c3e2af59f7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/main.py
@@ -0,0 +1,145 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Refine the strong initial grid using a force-directed simulation. This allows
+ # the circles to shift from the rigid grid into a more optimal packing.
+ centers = refine_centers_by_force(centers)
+
+ # Compute maximum valid radii for the refined configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def refine_centers_by_force(centers, n_iter=400, learning_rate=2e-5, wall_strength=0.4):
+ """
+ Refines circle centers using a vectorized force-directed simulation.
+ - Repulsion force is proportional to 1/distance.
+ - Wall force creates a potential well to keep circles inside the boundary.
+ """
+ n = centers.shape[0]
+
+ for sim_iter in range(n_iter):
+ # Vectorized calculation of repulsion forces (F ~ 1/r)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion
+
+ # Add a small epsilon to prevent division by zero for any coincident points
+ dist_sq = np.maximum(dist_sq, 1e-7)
+
+ inv_dist_sq = 1.0 / dist_sq
+ force_repel = np.sum(diffs * inv_dist_sq[:, :, np.newaxis], axis=1)
+
+ # Wall repulsion force to create a soft potential well
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Linearly anneal the learning rate
+ current_lr = learning_rate * (1.0 - (sim_iter / n_iter))
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += current_lr * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # Increased iterations to ensure full convergence for a dense packing.
+ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d2a47b71e26ff4a20c43db885428c2c8b80ec3da
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/original.py
@@ -0,0 +1,104 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Compute maximum valid radii for this superior configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # Increased iterations to ensure full convergence for a dense packing.
+ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a6f05a61006d65dd1fa3c9bb0df30fd9c3bad67f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_23/search_replace.txt
@@ -0,0 +1,89 @@
+
+add_force_directed_refinement
+
+
+
+The current program uses a static 5x5 grid with an additional circle placed in an interstitial void. While this is a strong initial configuration, its rigidity prevents the circles from arranging themselves into a more optimal, non-grid packing. The visualization clearly shows this, with circles constrained to their grid lines, resulting in suboptimal space utilization.
+
+To overcome this limitation, I am introducing a force-directed refinement step after the initial placement. This is a common and powerful technique in packing problems, inspired by physical simulations. The key ideas are:
+
+1. **Initial Placement**: Keep the excellent starting configuration of a 5x5 grid plus an interstitial circle at `(0.2, 0.2)`.
+2. **Force-Directed Relaxation**: After this initial placement, a simulation is run for 400 iterations to allow the circle centers to move and settle into a configuration with lower potential energy, which corresponds to a better packing.
+3. **Force Model**:
+ * **Repulsion Force**: A `1/r` repulsion force is applied between all pairs of circles to push them apart. This is implemented efficiently using vectorized numpy operations.
+ * **Wall Force**: A "potential well" wall force (`1/x - 1/(1-x)`) is used to smoothly repel circles from the boundaries, preventing them from getting stuck at the edges and encouraging a more central packing.
+4. **Annealing**: The learning rate of the simulation is gradually decreased over time (annealed) to allow for large adjustments at the beginning and fine-tuning at the end, helping the system settle into a good local minimum.
+
+By adding this relaxation phase, we transform the static constructor into a dynamic optimization process, allowing the circles to break the rigid grid symmetry and find a packing that fills the space more efficiently, which is expected to significantly increase the sum of radii. The hyperparameters for the simulation (`n_iter`, `learning_rate`, `wall_strength`) have been chosen based on analysis of prior successful programs that used similar techniques.
+
+
+
+<<<<<<< SEARCH
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Compute maximum valid radii for this superior configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+=======
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Refine the strong initial grid using a force-directed simulation. This allows
+ # the circles to shift from the rigid grid into a more optimal packing.
+ centers = refine_centers_by_force(centers)
+
+ # Compute maximum valid radii for the refined configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def refine_centers_by_force(centers, n_iter=400, learning_rate=2e-5, wall_strength=0.4):
+ """
+ Refines circle centers using a vectorized force-directed simulation.
+ - Repulsion force is proportional to 1/distance.
+ - Wall force creates a potential well to keep circles inside the boundary.
+ """
+ n = centers.shape[0]
+
+ for sim_iter in range(n_iter):
+ # Vectorized calculation of repulsion forces (F ~ 1/r)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion
+
+ # Add a small epsilon to prevent division by zero for any coincident points
+ dist_sq = np.maximum(dist_sq, 1e-7)
+
+ inv_dist_sq = 1.0 / dist_sq
+ force_repel = np.sum(diffs * inv_dist_sq[:, :, np.newaxis], axis=1)
+
+ # Wall repulsion force to create a soft potential well
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Linearly anneal the learning rate
+ current_lr = learning_rate * (1.0 - (sim_iter / n_iter))
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += current_lr * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers):
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d6d3244889dd024bd0932221f3955692aeddff03
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..f43a38e274e7f7604573775ee2315ec604fdd824
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/edit.diff
@@ -0,0 +1,276 @@
+--- a/original.py
++++ b/original.py
+@@ -1,139 +1,152 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
+-
+ import numpy as np
+
++class ConfigurableHybridPacker:
++ """
++ A class-based packer encapsulating state and logic for a hybrid
++ 'initialize-and-refine' strategy. This object-oriented structure improves
++ maintainability and separates concerns, with hyperparameters managed in a
++ centralized configuration dictionary.
++ """
++ def __init__(self, n, config):
++ """Initializes the packer with the number of circles and a config dict."""
++ self.n = n
++ self.config = config
++ self.centers = np.zeros((n, 2))
++ self.radii = np.zeros(n)
++
++ def _initialize_centers(self):
++ """Sets up the initial circle positions using the best-known static 5x5 grid."""
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ self.centers[k, 0] = (i + 0.5) * spacing
++ self.centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++ # Place the 26th circle in the optimal interstitial void found in prior runs.
++ self.centers[25] = [spacing, spacing]
++
++ def _compute_radii_for_centers(self, centers_to_eval):
++ """
++ Computes maximum radii for a given set of centers using iterative proportional scaling.
++ This is a core utility used both during and after the simulation.
++ """
++ radii = np.min([
++ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
++ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
++ ], axis=0)
++
++ for _ in range(self.config['radius_iter']):
++ updated = False
++ for i in range(self.n):
++ for j in range(i + 1, self.n):
++ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
++
++ if radii[i] + radii[j] > dist:
++ if dist < 1e-9: # Handle co-located centers
++ radii[i], radii[j] = 0.0, 0.0
++ else:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++ if not updated:
++ break
++ return np.maximum(radii, 0)
++
++ def _run_refinement_simulation(self):
++ """
++ Performs a force-directed simulation to refine circle positions.
++ This simulation uses a 'growth pressure' concept to actively
++ encourage circles to expand and fill empty space.
++ """
++ iterations = self.config['sim_iter']
++ base_lr = self.config['learning_rate']
++ wall_strength = self.config['wall_strength']
++ growth_pressure = self.config['growth_pressure']
++
++ for i_iter in range(iterations):
++ # 1. Calculate the maximum non-overlapping radii for the current positions.
++ current_radii = self._compute_radii_for_centers(self.centers)
++
++ # 2. Artificially inflate radii to create an expansion 'pressure'.
++ pressured_radii = current_radii * growth_pressure
++
++ forces = np.zeros_like(self.centers)
++
++ # 3. Calculate repulsion forces based on the artificial overlaps.
++ # a) Circle-to-circle repulsion
++ for i in range(self.n):
++ for j in range(i + 1, self.n):
++ vec = self.centers[i] - self.centers[j]
++ dist = np.linalg.norm(vec)
++ if dist < 1e-9: continue
++
++ overlap = pressured_radii[i] + pressured_radii[j] - dist
++ if overlap > 0:
++ force_magnitude = overlap
++ force_vec = (vec / dist) * force_magnitude
++ forces[i] += force_vec
++ forces[j] -= force_vec
++
++ # b) Wall repulsion
++ for i in range(self.n):
++ overlap_l = pressured_radii[i] - self.centers[i, 0]
++ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
++
++ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
++ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
++
++ overlap_b = pressured_radii[i] - self.centers[i, 1]
++ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
++
++ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
++ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
++
++ # 4. Update center positions with a decaying learning rate for stability.
++ lr = base_lr * (1.0 - (i_iter / iterations))**2
++ self.centers += forces * lr
++
++ # 5. Enforce hard boundary constraints.
++ self.centers = np.clip(self.centers, 0.0, 1.0)
++
++ def pack(self):
++ """Executes the full packing pipeline: initialize, refine, and finalize."""
++ self._initialize_centers()
++ self._run_refinement_simulation()
++ # Final computation of radii for the refined centers.
++ self.radii = self._compute_radii_for_centers(self.centers)
++ return self.centers, self.radii
+
+ def construct_packing():
+ """
+- Construct a specific arrangement of 26 circles in a unit square
+- that attempts to maximize the sum of their radii.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
++ Main function to construct the circle packing. This implementation shifts from
++ a procedural script to an object-oriented approach by using the
++ ConfigurableHybridPacker class to manage the packing process.
+ """
+- # Initialize arrays for 26 circles
+- n = 26
+-
+- # --- Refined Initial Placement Strategy ---
+- # Arrange 25 circles in a 5x5 grid.
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side # spacing is 0.2
+-
+- base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+- k = 0
+- for j in range(num_cells_side):
+- for i in range(num_cells_side):
+- base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+- base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+- k += 1
+-
+- # Define strategic candidate placements for the 26th circle.
+- # These are interstitial points and the center of the square.
+- # The interstitial points are derived from the 5x5 grid's structure.
+- # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+- candidate_extra_positions = [
+- [spacing, spacing], # (0.2, 0.2)
+- [spacing, 0.5], # (0.2, 0.5)
+- [spacing, 1 - spacing], # (0.2, 0.8)
+- [0.5, spacing], # (0.5, 0.2)
+- [0.5, 0.5], # (0.5, 0.5) - Center
+- [0.5, 1 - spacing], # (0.5, 0.8)
+- [1 - spacing, spacing], # (0.8, 0.2)
+- [1 - spacing, 0.5], # (0.8, 0.5)
+- [1 - spacing, 1 - spacing] # (0.8, 0.8)
+- ]
+-
+- best_sum_radii = 0
+- best_centers = None
+- best_radii = None
+-
+- # Iterate through candidate positions for the 26th circle to find the best one.
+- for extra_pos in candidate_extra_positions:
+- current_centers = np.zeros((n, 2))
+- current_centers[:25] = base_centers
+- current_centers[25] = extra_pos
+-
+- current_radii = compute_max_radii(current_centers)
+- current_sum_radii = np.sum(current_radii)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers = current_centers
+- best_radii = current_radii
+-
+- # Fallback if no valid configuration was found (should not happen with good candidates)
+- if best_centers is None:
+- centers = np.zeros((n, 2))
+- centers[:25] = base_centers
+- centers[25] = [0.5, 0.5] # Default to center for 26th circle
+- radii = compute_max_radii(centers)
+- else:
+- centers = best_centers
+- radii = best_radii
+-
++ # Hyperparameters are now centralized in a configuration dictionary,
++ # making the packer's behavior explicit and easier to tune.
++ packer_config = {
++ 'sim_iter': 500, # More iterations for thorough refinement.
++ 'radius_iter': 250, # Sufficient iterations for radius convergence.
++ 'learning_rate': 0.02, # A balanced learning rate for exploration.
++ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
++ 'growth_pressure': 1.005 # Gentle expansion pressure to fill gaps.
++ }
++
++ packer = ConfigurableHybridPacker(n=26, config=packer_config)
++ centers, radii = packer.pack()
++
+ return centers, radii
+-
+-
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence
+- for _ in range(500): # Increased iterations to 500 for better convergence
+- updated_in_pass = False
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- # If current radii would cause overlap and distance is not zero
+- if dist < 1e-9: # Handle cases where centers might be identical or very close
+- # If centers are practically the same, one circle must have radius 0
+- # For packing, it's safer to consider them as overlapping severely and shrink them
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- # Scale both radii proportionally to just touch
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- # If a full pass over all pairs results in no change, radii have stabilized
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0) # Ensure no negative radii
+-
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..1cc9c4e7647dbb9379a94f68bf51567224bb05ea
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/main.py
@@ -0,0 +1,152 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ growth_pressure = self.config['growth_pressure']
+
+ for i_iter in range(iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ vec = self.centers[i] - self.centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion
+ for i in range(self.n):
+ overlap_l = pressured_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary,
+ # making the packer's behavior explicit and easier to tune.
+ packer_config = {
+ 'sim_iter': 500, # More iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'growth_pressure': 1.005 # Gentle expansion pressure to fill gaps.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c62f05af02e898eb2fbc02f6d58c10848f6776b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/original.py
@@ -0,0 +1,139 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers
+ current_centers[25] = extra_pos
+
+ current_radii = compute_max_radii(current_centers)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = current_centers
+ best_radii = current_radii
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ centers = np.zeros((n, 2))
+ centers[:25] = base_centers
+ centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ radii = compute_max_radii(centers)
+ else:
+ centers = best_centers
+ radii = best_radii
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..8764ede8a2fec20797c1fafd2505b07b003ac8d4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results
+Run 1/1 completed in 2.55 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9285930448389967
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8997, 0.1003)\n centers[5] = (0.0983, 0.3017)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.5000, 0.7000)\n centers[18] = (0.6981, 0.6970)\n centers[19] = (0.9019, 0.7000)\n centers[20] = (0.0989, 0.8999)\n centers[21] = (0.2993, 0.8987)\n centers[22] = (0.5017, 0.8987)\n centers[23] = (0.7007, 0.9024)\n centers[24] = (0.8995, 0.8991)\n centers[25] = (0.2017, 0.1983)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9285930448389967}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/packing_viz.png
+ execution_time_mean: 2.5461575253866613
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..523ff07385f3b435eb341655083b5da5ae575bc7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9285930448389967,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8997, 0.1003)\n centers[5] = (0.0983, 0.3017)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.5000, 0.7000)\n centers[18] = (0.6981, 0.6970)\n centers[19] = (0.9019, 0.7000)\n centers[20] = (0.0989, 0.8999)\n centers[21] = (0.2993, 0.8987)\n centers[22] = (0.5017, 0.8987)\n centers[23] = (0.7007, 0.9024)\n centers[24] = (0.8995, 0.8991)\n centers[25] = (0.2017, 0.1983)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9285930448389967
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/results/packing_viz.png",
+ "execution_time_mean": 2.5461575253866613,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..50ea35412074895d399ccd9c9a668bb2e77923f4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_24/rewrite.txt
@@ -0,0 +1,143 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ growth_pressure = self.config['growth_pressure']
+
+ for i_iter in range(iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ vec = self.centers[i] - self.centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion
+ for i in range(self.n):
+ overlap_l = pressured_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary,
+ # making the packer's behavior explicit and easier to tune.
+ packer_config = {
+ 'sim_iter': 500, # More iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'growth_pressure': 1.005 # Gentle expansion pressure to fill gaps.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..8d455d3fc97758be748a36756806b77106dc06ac
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/edit.diff
@@ -0,0 +1,276 @@
+--- a/original.py
++++ b/original.py
+@@ -1,201 +1,250 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+- # --- Refined Initial Placement Strategy ---
+- # Arrange 25 circles in a 5x5 grid.
++ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+- # These are interstitial points and the center of the square.
+- # The interstitial points are derived from the 5x5 grid's structure.
+- # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
++ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
++ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+- [1 - spacing, 1 - spacing] # (0.8, 0.8)
++ [1 - spacing, 1 - spacing], # (0.8, 0.8)
++ # Corner-offset points
++ [0.05, 0.05],
++ [0.05, 0.95],
++ [0.95, 0.05],
++ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+- simulation_iterations = 500 # More simulation steps for better convergence
+- initial_learning_rate = 0.05 # Slightly more aggressive initial movement
+- wall_repulsion_strength = 0.5 # Kept from prior working simulations
+-
++ simulation_iterations = 500 # Number of steps for force-directed simulation
++ initial_learning_rate = 0.05 # Initial step size for center updates
++ wall_repulsion_strength = 0.5 # Strength of repulsion from square walls
++
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+- # 1. Calculate radii for the current center configuration
+- radii_for_sim = compute_max_radii(centers_for_sim)
++ # 1. Calculate approximate radii for force calculation (faster, fewer iterations)
++ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+- # a. Circle-to-circle repulsion forces
++ # a. Circle-to-circle repulsion forces (overlap-based)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+- continue # No force calculation needed, radii_for_sim handles overlap
++ # No force calculation needed, radii_for_sim handles overlap by setting radii to 0
++ continue
+
+ overlap = radii_for_sim[i] + radii_for_sim[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+- # b. Wall repulsion forces
++ # b. Wall repulsion forces (overlap-based)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+- # After simulation, use the refined centers to compute final radii
++ # After simulation, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+- # If the loop somehow failed to find any valid configuration
+- # This fallback needs to be reconsidered if simulation can lead to all radii being zero
+- # For now, it will use the base 5x5 + center as a safe default
++ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
++ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence
+- for _ in range(500): # Increased iterations to 500 for better convergence
++ # Increased iterations for better convergence for final radii
++ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+- # If centers are practically the same, one circle must have radius 0
+- # For packing, it's safer to consider them as overlapping severely and shrink them
++ # If centers are practically the same, they cannot both have positive radius.
++ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
++
++
++def compute_radii_for_simulation(centers):
++ """
++ Compute approximate maximum radii for each circle position
++ for use inside the simulation loop. Fewer iterations for performance.
++
++ Args:
++ centers: np.array of shape (n, 2) with (x, y) coordinates
++
++ Returns:
++ np.array of shape (n) with radius of each circle
++ """
++ n = centers.shape[0]
++ radii = np.ones(n)
++
++ # First, limit by distance to square borders
++ for i in range(n):
++ x, y = centers[i]
++ radii[i] = min(x, y, 1 - x, 1 - y)
++
++ # Then, limit by distance to other circles using iterative proportional scaling
++ # Reduced iterations for faster computation during simulation
++ for _ in range(100):
++ updated_in_pass = False
++ for i in range(n):
++ for j in range(i + 1, n):
++ dist = np.linalg.norm(centers[i] - centers[j])
++
++ if dist < 1e-9:
++ radii[i] = 0
++ radii[j] = 0
++ updated_in_pass = True
++ continue
++
++ if radii[i] + radii[j] > dist:
++ updated_in_pass = True
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++
++ if not updated_in_pass:
++ break
++
++ return np.maximum(radii, 0)
++
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3868b51b38de664900c6d049515e3168f7b57b7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/main.py
@@ -0,0 +1,250 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+ initial_learning_rate = 0.05 # Initial step size for center updates
+ wall_repulsion_strength = 0.5 # Strength of repulsion from square walls
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate approximate radii for force calculation (faster, fewer iterations)
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (overlap-based)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+ # No force calculation needed, radii_for_sim handles overlap by setting radii to 0
+ continue
+
+ overlap = radii_for_sim[i] + radii_for_sim[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (overlap-based)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Reduced iterations for faster computation during simulation
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..59273f5344ff344d766f71645eebc3282e2764ce
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/original.py
@@ -0,0 +1,201 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # More simulation steps for better convergence
+ initial_learning_rate = 0.05 # Slightly more aggressive initial movement
+ wall_repulsion_strength = 0.5 # Kept from prior working simulations
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current center configuration
+ radii_for_sim = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+ continue # No force calculation needed, radii_for_sim handles overlap
+
+ overlap = radii_for_sim[i] + radii_for_sim[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute final radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # If the loop somehow failed to find any valid configuration
+ # This fallback needs to be reconsidered if simulation can lead to all radii being zero
+ # For now, it will use the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..7ade04ec974d0bb239212f45d9188a78c9a81f56
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results
+Run 1/1 completed in 68.93 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9169561144739513
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.9000, 0.1000)\n centers[5] = (0.1000, 0.3000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.5000, 0.7000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.9000, 0.7000)\n centers[20] = (0.1000, 0.9000)\n centers[21] = (0.3000, 0.9000)\n centers[22] = (0.5000, 0.9000)\n centers[23] = (0.7000, 0.9000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.2000, 0.2000)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9169561144739513}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/packing_viz.png
+ execution_time_mean: 68.9269751990214
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..f581fa2ed1bc5b6e2a610a1c195898e71f09b766
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9169561144739513,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.9000, 0.1000)\n centers[5] = (0.1000, 0.3000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.5000, 0.7000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.9000, 0.7000)\n centers[20] = (0.1000, 0.9000)\n centers[21] = (0.3000, 0.9000)\n centers[22] = (0.5000, 0.9000)\n centers[23] = (0.7000, 0.9000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.2000, 0.2000)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9169561144739513
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/results/packing_viz.png",
+ "execution_time_mean": 68.9269751990214,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a3868b51b38de664900c6d049515e3168f7b57b7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_25/rewrite.txt
@@ -0,0 +1,250 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+ initial_learning_rate = 0.05 # Initial step size for center updates
+ wall_repulsion_strength = 0.5 # Strength of repulsion from square walls
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate approximate radii for force calculation (faster, fewer iterations)
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (overlap-based)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+ # No force calculation needed, radii_for_sim handles overlap by setting radii to 0
+ continue
+
+ overlap = radii_for_sim[i] + radii_for_sim[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (overlap-based)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Reduced iterations for faster computation during simulation
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..b52ea3198cc73d1fe64af746405732bcaa34aa9c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/edit.diff
@@ -0,0 +1,263 @@
+--- a/original.py
++++ b/original.py
+@@ -1,200 +1,252 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ Improved by combining insights from high-scoring prior programs:
+ - Optimized initial placement of the 26th circle.
+ - Reduced initial perturbation noise.
+ - Tuned learning rate for fine-grained adjustments.
+ - Increased iterations for radius calculation convergence.
+ - Robust handling of very close circle centers in radius calculation.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+- growth_pressure = 1.005 # Reintroduced from 1.92 program for denser packing
++ initial_growth_pressure = 1.01 # Start with slightly higher pressure to aggressively expand
++ final_growth_pressure = 1.001 # End with a smaller pressure to allow finer adjustments
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+- # Create "inflated" radii to generate repulsive forces even when just touching
+- inflated_radii = radii * growth_pressure
++ # Apply adaptive growth pressure: anneal from initial_growth_pressure to final_growth_pressure
++ current_growth_pressure = initial_growth_pressure - \
++ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
++ inflated_radii = radii * current_growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Use inflated radii for overlap calculation
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+- # Return the best configuration found across all trials
+- final_centers = best_final_centers
+- final_radii = best_final_radii
++ # --- Post-Simulation Fine-Tuning Phase ---
++ # Run a few more iterations with a very small learning rate and no growth pressure
++ # to allow centers to settle precisely, without aggressive expansion.
++ fine_tune_iterations = 50
++ fine_tune_learning_rate = 0.001
++
++ centers_for_sim = best_final_centers.copy() # Start fine-tuning from the best configuration
++
++ for _ in range(fine_tune_iterations):
++ radii = compute_max_radii(centers_for_sim)
++ forces = np.zeros((n, 2))
++
++ # a. Circle-to-circle repulsion forces (using actual radii, no growth pressure)
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers_for_sim[i] - centers_for_sim[j]
++ dist = np.linalg.norm(vec)
++ if dist < 1e-9: continue
++
++ overlap = radii[i] + radii[j] - dist
++
++ if overlap > 0:
++ force_magnitude = overlap
++ force_vec = (vec / dist) * force_magnitude
++ forces[i] += force_vec
++ forces[j] -= force_vec
++
++ # b. Wall repulsion forces (using actual radii, no growth pressure)
++ for i in range(n):
++ # Left wall
++ overlap_left = radii[i] - centers_for_sim[i, 0]
++ if overlap_left > 0:
++ forces[i, 0] += wall_repulsion_strength * overlap_left
++ # Right wall
++ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
++ if overlap_right > 0:
++ forces[i, 0] -= wall_repulsion_strength * overlap_right
++ # Bottom wall
++ overlap_bottom = radii[i] - centers_for_sim[i, 1]
++ if overlap_bottom > 0:
++ forces[i, 1] += wall_repulsion_strength * overlap_bottom
++ # Top wall
++ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
++ if overlap_top > 0:
++ forces[i, 1] -= wall_repulsion_strength * overlap_top
++
++ centers_for_sim += forces * fine_tune_learning_rate
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # After fine-tuning, perform a final radius calculation
++ final_centers = centers_for_sim
++ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence (from 100 to 200)
+- for _ in range(200):
++ # Increased iterations for better convergence (from 200 to 500, aligning with higher scores)
++ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..861af42813fcaee1fd069a72f6ed93551c54fc00
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/main.py
@@ -0,0 +1,252 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01 # Start with slightly higher pressure to aggressively expand
+ final_growth_pressure = 1.001 # End with a smaller pressure to allow finer adjustments
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Apply adaptive growth pressure: anneal from initial_growth_pressure to final_growth_pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Use inflated radii for overlap calculation
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Run a few more iterations with a very small learning rate and no growth pressure
+ # to allow centers to settle precisely, without aggressive expansion.
+ fine_tune_iterations = 50
+ fine_tune_learning_rate = 0.001
+
+ centers_for_sim = best_final_centers.copy() # Start fine-tuning from the best configuration
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After fine-tuning, perform a final radius calculation
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 200 to 500, aligning with higher scores)
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e78a43f372d7dd124e17e2c41d226d246b584f8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/original.py
@@ -0,0 +1,200 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+ growth_pressure = 1.005 # Reintroduced from 1.92 program for denser packing
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Use inflated radii for overlap calculation
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # Return the best configuration found across all trials
+ final_centers = best_final_centers
+ final_radii = best_final_radii
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3468d40d5137e11928257ab084325857e191c448
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_26/search_replace.txt
@@ -0,0 +1,193 @@
+
+
+Increase the number of iterations in `compute_max_radii` from 200 to 500. This follows the recommendation to increase iteration counts for radius calculation, which has correlated with higher scores in previous programs. More iterations allow for a more stable and accurate determination of maximum radii given the circle centers, leading to better force calculations in the main simulation loop.
+
+
+<<<<<<< SEARCH
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+=======
+ # Increased iterations for better convergence (from 200 to 500, aligning with higher scores)
+ for _ in range(500):
+>>>>>>> REPLACE
+
+
+
+
+Implement an annealing schedule for `growth_pressure`. Start with a slightly higher value (e.g., `1.01`) to aggressively push circles apart and resolve overlaps early, and then gradually reduce it towards `1.001` as the simulation progresses. This allows for coarse adjustments early on and fine-tuning later, similar to the learning rate annealing. This directly addresses the recommendation to implement adaptive annealing for `growth_pressure`.
+
+
+<<<<<<< SEARCH
+ growth_pressure = 1.005 # Reintroduced from 1.92 program for denser packing
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+=======
+ initial_growth_pressure = 1.01 # Start with slightly higher pressure to aggressively expand
+ final_growth_pressure = 1.001 # End with a smaller pressure to allow finer adjustments
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Apply adaptive growth pressure: anneal from initial_growth_pressure to final_growth_pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+>>>>>>> REPLACE
+
+
+
+
+Add a post-simulation fine-tuning phase. After the main simulation loop concludes for all candidate initial positions, run a small number of additional iterations (e.g., 50) with a very small fixed learning rate and no growth pressure to allow the circles to settle into their final positions without aggressive expansion, purely optimizing for fit. This might help escape shallow local minima after the primary annealing process.
+
+
+<<<<<<< SEARCH
+ # Return the best configuration found across all trials
+ final_centers = best_final_centers
+ final_radii = best_final_radii
+
+ return final_centers, final_radii
+=======
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Run a few more iterations with a very small learning rate and no growth pressure
+ # to allow centers to settle precisely, without aggressive expansion.
+ fine_tune_iterations = 50
+ fine_tune_learning_rate = 0.001
+
+ centers_for_sim = best_final_centers.copy() # Start fine-tuning from the best configuration
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After fine-tuning, perform a final radius calculation
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..b4ad62b005252d1c375f18fb0fc04e6f380954f3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/edit.diff
@@ -0,0 +1,266 @@
+--- a/original.py
++++ b/original.py
+@@ -1,191 +1,173 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ Improved by combining insights from high-scoring prior programs:
+ - Optimized initial placement of the 26th circle.
+ - Reduced initial perturbation noise.
+ - Tuned learning rate for fine-grained adjustments.
+ - Increased iterations for radius calculation convergence.
+ - Robust handling of very close circle centers in radius calculation.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+- # --- Hyperparameters for the simulation ---
+- iterations = 250 # Number of simulation steps (kept from current)
+- learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+- wall_repulsion_strength = 0.5 # Kept from current
++ # --- Hyperparameters for the simulation (tuned from high-scoring program) ---
++ iterations = 500 # Increased iterations for better convergence
++ learning_rate = 0.02 # A balanced learning rate for exploration.
++ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
++ growth_pressure = 1.005 # Key parameter: gentle expansion pressure to fill gaps.
+
+- best_sum_radii = -1.0
+- best_final_centers = None
+- best_final_radii = None
++ # --- Initialization ---
++ # Start with a 5x5 grid plus one interstitial circle. This is a proven high-potential starting configuration.
++ centers = np.zeros((n, 2))
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for i in range(num_cells_side):
++ for j in range(num_cells_side):
++ centers[k, 0] = (i + 0.5) * spacing
++ centers[k, 1] = (j + 0.5) * spacing
++ k += 1
+
+- # Candidate initial positions for the 26th circle to optimize corner utilization.
+- # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+- candidate_extra_circle_initial_positions = [
+- [0.05, 0.05], # Original position
+- [0.05, 0.95], # Top-left corner
+- [0.95, 0.05], # Bottom-right corner
+- [0.95, 0.95], # Top-right corner
+- ]
++ # Place the 26th circle in the interstitial void at (0.2, 0.2)
++ centers[25] = [spacing, spacing]
+
+- for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+- # --- Initialization for current trial ---
+- # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+- # Re-initialize centers for each trial to ensure independent simulations.
+- current_centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- current_centers[k, 0] = (i + 0.5) * spacing
+- current_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
++ # --- Simulation Loop ---
++ # The simulation refines center positions using a force-directed layout algorithm.
++ # It uses a "growth pressure" concept to actively seek more efficient packings.
++ for sim_iter in range(iterations):
++ # 1. Calculate the maximum non-overlapping radii for the current positions.
++ radii = compute_max_radii(centers)
+
+- # Place the 26th circle at the current candidate position.
+- current_centers[25] = initial_pos_26th_circle
++ # 2. Artificially inflate radii to create an expansion 'pressure'.
++ # This is the key to escaping local minima and finding denser packings.
++ pressured_radii = radii * growth_pressure
+
+- # Use a copy of centers for the simulation, as it modifies the array
+- centers_for_sim = current_centers.copy()
++ # 3. Calculate forces to push centers to a better configuration.
++ forces = np.zeros((n, 2))
+
+- # --- Simulation Loop ---
+- for sim_iter in range(iterations):
+- # 1. Calculate radii for the current center configuration
+- radii = compute_max_radii(centers_for_sim)
++ # a. Circle-to-circle repulsion based on artificial overlap
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers[i] - centers[j]
++ dist = np.linalg.norm(vec)
+
+- # 2. Calculate forces to push centers to a better configuration
+- forces = np.zeros((n, 2))
++ if dist < 1e-9:
++ continue
+
+- # a. Circle-to-circle repulsion forces
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers_for_sim[i] - centers_for_sim[j]
+- dist = np.linalg.norm(vec)
++ # Calculate overlap using the inflated, 'pressured' radii
++ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+- if dist < 1e-9:
+- continue
++ if overlap > 0:
++ force_magnitude = overlap
++ force_vec = (vec / dist) * force_magnitude
++ forces[i] += force_vec
++ forces[j] -= force_vec
+
+- overlap = radii[i] + radii[j] - dist
++ # b. Wall repulsion forces, also using pressured radii
++ for i in range(n):
++ # Left wall
++ overlap_left = pressured_radii[i] - centers[i, 0]
++ if overlap_left > 0:
++ forces[i, 0] += wall_repulsion_strength * overlap_left
++ # Right wall
++ overlap_right = (centers[i, 0] + pressured_radii[i]) - 1.0
++ if overlap_right > 0:
++ forces[i, 0] -= wall_repulsion_strength * overlap_right
++ # Bottom wall
++ overlap_bottom = pressured_radii[i] - centers[i, 1]
++ if overlap_bottom > 0:
++ forces[i, 1] += wall_repulsion_strength * overlap_bottom
++ # Top wall
++ overlap_top = (centers[i, 1] + pressured_radii[i]) - 1.0
++ if overlap_top > 0:
++ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec
++ # 4. Update center positions with an annealing learning rate for stability.
++ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
++ centers += forces * current_lr
+
+- # b. Wall repulsion forces
+- for i in range(n):
+- # Left wall
+- overlap_left = radii[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = radii[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
++ # 5. Enforce hard boundary constraints.
++ centers = np.clip(centers, 0.0, 1.0)
+
+- # 3. Update center positions based on forces
+- current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+- centers_for_sim += forces * current_lr
+-
+- # 4. Enforce boundary constraints - keep centers within [0,1]
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # --- Finalization for current trial ---
+- current_final_radii = compute_max_radii(centers_for_sim)
+- current_sum_radii = np.sum(current_final_radii)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_final_centers = centers_for_sim.copy() # Store a copy
+- best_final_radii = current_final_radii.copy() # Store a copy
+-
+- # Return the best configuration found across all trials
+- final_centers = best_final_centers
+- final_radii = best_final_radii
++ # --- Finalization ---
++ # After the simulation, compute the final, precise radii for the optimized centers.
++ final_radii = compute_max_radii(centers)
++ final_centers = centers
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence (from 100 to 200)
+- for _ in range(200):
++ # Increased iterations for better convergence, matching high-scoring programs.
++ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..46e6498ef4412e636b351042d79badbab6737584
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/main.py
@@ -0,0 +1,173 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation (tuned from high-scoring program) ---
+ iterations = 500 # Increased iterations for better convergence
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ growth_pressure = 1.005 # Key parameter: gentle expansion pressure to fill gaps.
+
+ # --- Initialization ---
+ # Start with a 5x5 grid plus one interstitial circle. This is a proven high-potential starting configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the interstitial void at (0.2, 0.2)
+ centers[25] = [spacing, spacing]
+
+ # --- Simulation Loop ---
+ # The simulation refines center positions using a force-directed layout algorithm.
+ # It uses a "growth pressure" concept to actively seek more efficient packings.
+ for sim_iter in range(iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ radii = compute_max_radii(centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the key to escaping local minima and finding denser packings.
+ pressured_radii = radii * growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration.
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion based on artificial overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Calculate overlap using the inflated, 'pressured' radii
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using pressured radii
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 4. Update center positions with an annealing learning rate for stability.
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 5. Enforce hard boundary constraints.
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, compute the final, precise radii for the optimized centers.
+ final_radii = compute_max_radii(centers)
+ final_centers = centers
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence, matching high-scoring programs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae84d715fe232fbd4c97402fede1c9092e3d5047
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/original.py
@@ -0,0 +1,191 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250 # Number of simulation steps (kept from current)
+ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+ wall_repulsion_strength = 0.5 # Kept from current
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # Return the best configuration found across all trials
+ final_centers = best_final_centers
+ final_radii = best_final_radii
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ecfbf48b63aa012517ff08907a5abb900b13f330
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_27/search_replace.txt
@@ -0,0 +1,249 @@
+
+adopt_growth_pressure_simulation
+
+
+This edit overhauls the simulation logic by adopting the 'growth pressure' model from the highest-scoring prior program (score 1.93). The original simulation was inert because it only resolved existing overlaps, but the `compute_max_radii` function already ensures no overlaps exist, resulting in zero forces.
+
+The new logic works as follows:
+1. Calculate the maximum non-overlapping radii.
+2. Artificially inflate these radii by a `growth_pressure` factor (1.005).
+3. Calculate repulsion forces based on the overlaps of these *inflated* radii.
+
+This creates an active expansion force, pushing circles apart to fill empty space and seek more efficient packings.
+
+Additionally, this edit incorporates other successful hyperparameters from the same high-scoring program:
+- **Initial Placement**: Replaces the ineffective corner-placement search with a single, proven initial configuration: a 5x5 grid with the 26th circle at the interstitial site `(0.2, 0.2)`.
+- **Simulation Iterations**: Increased from 250 to 500 for more thorough refinement.
+- **Wall Strength**: Increased from 0.5 to 0.8 to better utilize boundaries.
+- **Radius Calculation**: Increased `compute_max_radii` iterations from 200 to 250 for better convergence.
+
+This comprehensive change fixes the core flaw of the previous simulation and aligns the algorithm with a proven, high-performance strategy.
+
+
+<<<<<<< SEARCH
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250 # Number of simulation steps (kept from current)
+ learning_rate = 0.02 # Reduced learning rate for finer adjustments (from 1.87 program)
+ wall_repulsion_strength = 0.5 # Kept from current
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # Return the best configuration found across all trials
+ final_centers = best_final_centers
+ final_radii = best_final_radii
+
+ return final_centers, final_radii
+=======
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation (tuned from high-scoring program) ---
+ iterations = 500 # Increased iterations for better convergence
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ growth_pressure = 1.005 # Key parameter: gentle expansion pressure to fill gaps.
+
+ # --- Initialization ---
+ # Start with a 5x5 grid plus one interstitial circle. This is a proven high-potential starting configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the interstitial void at (0.2, 0.2)
+ centers[25] = [spacing, spacing]
+
+ # --- Simulation Loop ---
+ # The simulation refines center positions using a force-directed layout algorithm.
+ # It uses a "growth pressure" concept to actively seek more efficient packings.
+ for sim_iter in range(iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ radii = compute_max_radii(centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the key to escaping local minima and finding denser packings.
+ pressured_radii = radii * growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration.
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion based on artificial overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Calculate overlap using the inflated, 'pressured' radii
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using pressured radii
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 4. Update center positions with an annealing learning rate for stability.
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 5. Enforce hard boundary constraints.
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, compute the final, precise radii for the optimized centers.
+ final_radii = compute_max_radii(centers)
+ final_centers = centers
+
+ return final_centers, final_radii
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+=======
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence, matching high-scoring programs.
+ for _ in range(250):
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5019f22740c5d16d4f8a575b5949066d4d008c16
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..56d7cb7dc7235f4dede47c70d19f75d70decdaa9
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/edit.diff
@@ -0,0 +1,273 @@
+--- a/original.py
++++ b/original.py
+@@ -1,250 +1,256 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
++ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+- initial_learning_rate = 0.05 # Initial step size for center updates
+- wall_repulsion_strength = 0.5 # Strength of repulsion from square walls
++ initial_learning_rate = 0.02 # A more stable learning rate for refinement.
++ wall_repulsion_strength = 0.8 # Stronger walls to utilize boundary space.
++ growth_pressure = 1.005 # Key parameter to create expansion force.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+- # 1. Calculate approximate radii for force calculation (faster, fewer iterations)
++ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
++
++ # 1.5. Artificially inflate radii to create an expansion 'pressure'.
++ # This is the crucial step that drives the simulation.
++ pressured_radii = radii_for_sim * growth_pressure
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+- # a. Circle-to-circle repulsion forces (overlap-based)
++ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+- if dist < 1e-9: # Centers are too close, effectively identical
+- # No force calculation needed, radii_for_sim handles overlap by setting radii to 0
++ if dist < 1e-9: # Centers are too close.
+ continue
+
+- overlap = radii_for_sim[i] + radii_for_sim[j] - dist
++ # Calculate overlap based on the artificially inflated radii.
++ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+- # b. Wall repulsion forces (overlap-based)
++ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+- overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
++ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+- overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
++ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+- overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
++ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+- overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
++ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+ def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- # Reduced iterations for faster computation during simulation
+- for _ in range(100):
++ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
++ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b901cf614ca947e5bc73a16b33ef8ab284cd09e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/main.py
@@ -0,0 +1,256 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+ initial_learning_rate = 0.02 # A more stable learning rate for refinement.
+ wall_repulsion_strength = 0.8 # Stronger walls to utilize boundary space.
+ growth_pressure = 1.005 # Key parameter to create expansion force.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 1.5. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the crucial step that drives the simulation.
+ pressured_radii = radii_for_sim * growth_pressure
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ # Calculate overlap based on the artificially inflated radii.
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..ac056b86298abd4fd0156c7e90ea8d732f0bfa1e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/original.py
@@ -0,0 +1,250 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+ initial_learning_rate = 0.05 # Initial step size for center updates
+ wall_repulsion_strength = 0.5 # Strength of repulsion from square walls
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate approximate radii for force calculation (faster, fewer iterations)
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (overlap-based)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+ # No force calculation needed, radii_for_sim handles overlap by setting radii to 0
+ continue
+
+ overlap = radii_for_sim[i] + radii_for_sim[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (overlap-based)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Reduced iterations for faster computation during simulation
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..9587f1cf2d2bf55845f75efb3e143a4115a9cd7b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results
+Run 1/1 completed in 146.92 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9285930448389967
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8997, 0.1003)\n centers[5] = (0.0983, 0.3017)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.5000, 0.7000)\n centers[18] = (0.6981, 0.6970)\n centers[19] = (0.9019, 0.7000)\n centers[20] = (0.0989, 0.8999)\n centers[21] = (0.2993, 0.8987)\n centers[22] = (0.5017, 0.8987)\n centers[23] = (0.7007, 0.9024)\n centers[24] = (0.8995, 0.8991)\n centers[25] = (0.2017, 0.1983)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9285930448389967}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/packing_viz.png
+ execution_time_mean: 146.92364939907566
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..e2b56376e032b7e34e97d4f88337861ad3ddada0
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9285930448389967,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8997, 0.1003)\n centers[5] = (0.0983, 0.3017)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.5000, 0.7000)\n centers[18] = (0.6981, 0.6970)\n centers[19] = (0.9019, 0.7000)\n centers[20] = (0.0989, 0.8999)\n centers[21] = (0.2993, 0.8987)\n centers[22] = (0.5017, 0.8987)\n centers[23] = (0.7007, 0.9024)\n centers[24] = (0.8995, 0.8991)\n centers[25] = (0.2017, 0.1983)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9285930448389967
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/results/packing_viz.png",
+ "execution_time_mean": 146.92364939907566,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..57f2409ef5e314672f7d47fbdc508e94d5733c58
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_28/search_replace.txt
@@ -0,0 +1,140 @@
+
+activate_growth_pressure_simulation
+
+
+
+The current program's force-directed simulation is inactive because it calculates forces based on non-overlapping radii, resulting in zero force. This edit fixes this fundamental flaw by introducing a "growth pressure" mechanism, a concept proven effective in the top-scoring prior program (1.93).
+
+1. **Introduce `growth_pressure`:** A new hyperparameter `growth_pressure = 1.005` is added. Inside the simulation loop, the calculated `radii_for_sim` are artificially inflated by this factor to create `pressured_radii`.
+2. **Apply Pressure to Forces:** The overlap calculations for both circle-circle and circle-wall repulsions are now based on these `pressured_radii`. This generates repulsive forces even when circles are not technically overlapping, pushing them apart to seek configurations with larger potential radii.
+3. **Tune Hyperparameters:** To support this new force model, other simulation parameters (`initial_learning_rate`, `wall_repulsion_strength`) are tuned to match the values from the successful 1.93 program, promoting stable and effective refinement.
+4. **Improve Simulation Accuracy:** The number of iterations in `compute_radii_for_simulation` is increased from 100 to 250. This provides a more accurate basis for the force calculations at each step, leading to a better-quality final optimization, as demonstrated by prior high-scoring programs.
+
+This set of changes reactivates and empowers the simulation, allowing it to properly refine the excellent initial grid placement and significantly improve the sum of radii.
+
+
+
+<<<<<<< SEARCH
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+ initial_learning_rate = 0.05 # Initial step size for center updates
+ wall_repulsion_strength = 0.5 # Strength of repulsion from square walls
+=======
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+ initial_learning_rate = 0.02 # A more stable learning rate for refinement.
+ wall_repulsion_strength = 0.8 # Stronger walls to utilize boundary space.
+ growth_pressure = 1.005 # Key parameter to create expansion force.
+>>>>>>> REPLACE
+
+
+
+<<<<<<< SEARCH
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate approximate radii for force calculation (faster, fewer iterations)
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (overlap-based)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+ # No force calculation needed, radii_for_sim handles overlap by setting radii to 0
+ continue
+
+ overlap = radii_for_sim[i] + radii_for_sim[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (overlap-based)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+=======
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 1.5. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the crucial step that drives the simulation.
+ pressured_radii = radii_for_sim * growth_pressure
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ # Calculate overlap based on the artificially inflated radii.
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+>>>>>>> REPLACE
+
+
+
+<<<<<<< SEARCH
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Reduced iterations for faster computation during simulation
+ for _ in range(100):
+=======
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4da8811534cff658aefe7c5ae084138f13d3a05b
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..92aad102c60552bd313eb353f53dcb336b284135
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/edit.diff
@@ -0,0 +1,326 @@
+--- a/original.py
++++ b/original.py
+@@ -1,252 +1,199 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+-Improved by combining insights from high-scoring prior programs:
+-- Optimized initial placement of the 26th circle.
+-- Reduced initial perturbation noise.
+-- Tuned learning rate for fine-grained adjustments.
+-- Increased iterations for radius calculation convergence.
+-- Robust handling of very close circle centers in radius calculation.
++This version is a crossover, combining the high-level strategy of the best-performing
++parent (multi-start, two-phase simulation, annealed growth pressure) with the
++efficient vectorized force calculations from the inspiration program.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+- learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
++ # Increased iterations due to vectorized efficiency
++ iterations = 750
++ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+- initial_growth_pressure = 1.01 # Start with slightly higher pressure to aggressively expand
+- final_growth_pressure = 1.001 # End with a smaller pressure to allow finer adjustments
++ initial_growth_pressure = 1.01
++ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+- best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+- # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+- [0.05, 0.05], # Original effective position
+- [0.05, 0.95], # Top-left corner
+- [0.95, 0.05], # Bottom-right corner
+- [0.95, 0.95], # Top-right corner
+- [0.2, 0.2], # Position that yielded 1.92 in a prior program
+- [0.2, 0.8], # Symmetric to [0.2, 0.2]
+- [0.8, 0.2], # Symmetric to [0.2, 0.2]
+- [0.8, 0.8], # Symmetric to [0.2, 0.2]
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
++ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+- # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+- # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+-
+- # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+- # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+- # --- Simulation Loop ---
++ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+- # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+-
+- # 2. Calculate forces to push centers to a better configuration
+- forces = np.zeros((n, 2))
+-
+- # Apply adaptive growth pressure: anneal from initial_growth_pressure to final_growth_pressure
++
++ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
++ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers_for_sim[i] - centers_for_sim[j]
+- dist = np.linalg.norm(vec)
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
++
++ overlaps = np.maximum(0, sum_inflated_radii - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
+
+- if dist < 1e-9:
+- continue
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
+
+- # Use inflated radii for overlap calculation
+- overlap = inflated_radii[i] + inflated_radii[j] - dist
++ # b. Vectorized Wall repulsion forces
++ wall_forces = np.zeros_like(centers_for_sim)
++ overlap_left = inflated_radii - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
++ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec
++ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
++ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
++ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
++ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+- # b. Wall repulsion forces, also using inflated radii
+- for i in range(n):
+- # Left wall
+- overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
+-
+- # 3. Update center positions based on forces
++ forces = circle_forces + wall_forces
++
++ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+-
+- # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+- best_final_centers = centers_for_sim.copy() # Store a copy
+- best_final_radii = current_final_radii.copy() # Store a copy
++ best_final_centers = centers_for_sim.copy()
+
+- # --- Post-Simulation Fine-Tuning Phase ---
+- # Run a few more iterations with a very small learning rate and no growth pressure
+- # to allow centers to settle precisely, without aggressive expansion.
+- fine_tune_iterations = 50
++ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
++ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+-
+- centers_for_sim = best_final_centers.copy() # Start fine-tuning from the best configuration
++ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+- forces = np.zeros((n, 2))
+
+- # a. Circle-to-circle repulsion forces (using actual radii, no growth pressure)
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers_for_sim[i] - centers_for_sim[j]
+- dist = np.linalg.norm(vec)
+- if dist < 1e-9: continue
++ # --- Vectorized Force Calculation (no growth pressure) ---
++ # a. Circle-to-circle forces
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+- overlap = radii[i] + radii[j] - dist
++ overlaps = np.maximum(0, sum_radii_pairs - dists)
++ np.fill_diagonal(overlaps, 0)
+
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
++
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
+
+- # b. Wall repulsion forces (using actual radii, no growth pressure)
+- for i in range(n):
+- # Left wall
+- overlap_left = radii[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = radii[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
++ # b. Wall forces
++ wall_forces = np.zeros_like(centers_for_sim)
++ overlap_left = radii - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
++ overlap_bottom = radii - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
++
++ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
++ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
++ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
++ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
++
++ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+- # After fine-tuning, perform a final radius calculation
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+- # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence (from 200 to 500, aligning with higher scores)
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+- # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+- # If centers are practically the same, they cannot both have positive radius.
+- # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+- continue # Skip to next pair
++ continue
+
+- # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+- # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+- # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+- return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
++ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..648682d4ca9de4b97c1ed5073dbaed558d912fc7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/main.py
@@ -0,0 +1,199 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..861af42813fcaee1fd069a72f6ed93551c54fc00
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/original.py
@@ -0,0 +1,252 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01 # Start with slightly higher pressure to aggressively expand
+ final_growth_pressure = 1.001 # End with a smaller pressure to allow finer adjustments
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Apply adaptive growth pressure: anneal from initial_growth_pressure to final_growth_pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Use inflated radii for overlap calculation
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Run a few more iterations with a very small learning rate and no growth pressure
+ # to allow centers to settle precisely, without aggressive expansion.
+ fine_tune_iterations = 50
+ fine_tune_learning_rate = 0.001
+
+ centers_for_sim = best_final_centers.copy() # Start fine-tuning from the best configuration
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After fine-tuning, perform a final radius calculation
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 200 to 500, aligning with higher scores)
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..e5ed53e7225d52f8aa6a5ebf5af88a9bcc202260
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results
+Run 1/1 completed in 16.35 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9294423821939175
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1004, 0.8996)\n centers[5] = (0.3020, 0.0980)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.6967, 0.6974)\n centers[19] = (0.7000, 0.9027)\n centers[20] = (0.8998, 0.0988)\n centers[21] = (0.8990, 0.2992)\n centers[22] = (0.8990, 0.5017)\n centers[23] = (0.9028, 0.7007)\n centers[24] = (0.8993, 0.8995)\n centers[25] = (0.1980, 0.2020)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9294423821939175}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/packing_viz.png
+ execution_time_mean: 16.345091931987554
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..0d0bb97a33f2c932beba6c8759032c0cae0a7f99
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9294423821939175,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1004, 0.8996)\n centers[5] = (0.3020, 0.0980)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.6967, 0.6974)\n centers[19] = (0.7000, 0.9027)\n centers[20] = (0.8998, 0.0988)\n centers[21] = (0.8990, 0.2992)\n centers[22] = (0.8990, 0.5017)\n centers[23] = (0.9028, 0.7007)\n centers[24] = (0.8993, 0.8995)\n centers[25] = (0.1980, 0.2020)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9294423821939175
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/results/packing_viz.png",
+ "execution_time_mean": 16.345091931987554,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..20e0195f01a93e32f7039acad277bdd1e3443994
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_29/rewrite.txt
@@ -0,0 +1,190 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b043c206ebadd79d3d448edcb5a4be427e3e5939
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..075e6d4949193895f52040381ac8739f1487cbf4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/edit.diff
@@ -0,0 +1,260 @@
+--- a/original.py
++++ b/original.py
+@@ -1,94 +1,198 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
+
+-import numpy as np
++def compute_max_radii(centers, max_iterations=50, tolerance=1e-7):
++ """
++ Computes radii for a given set of fixed circle centers such that:
++ 1. All circles are within the unit square.
++ 2. No circles overlap.
++ 3. The sum of radii is locally maximized (greedy growth heuristic).
+
++ This function is used as the fitness evaluator within the Simulated Annealing process.
++
++ Args:
++ centers: np.array of shape (n, 2) with (x, y) coordinates of circle centers.
++ max_iterations: Maximum number of iterations for the greedy growth process.
++ A higher value leads to more accurate radii but slower evaluation.
++ tolerance: Convergence tolerance for radii changes. If changes are smaller than this,
++ the iteration stops.
++
++ Returns:
++ np.array of shape (n) with radius of each circle. Returns zeros if any center
++ is outside the unit square such that a valid positive radius is impossible.
++ """
++ n = centers.shape[0]
++
++ # Step 1: Initialize radii based on wall constraints.
++ # Each radius is limited by the distance to the closest wall.
++ # This also acts as an initial check for centers being validly placed within the square.
++ initial_radii_from_walls = np.array([
++ min(c[0], 1 - c[0], c[1], 1 - c[1]) for c in centers
++ ])
++
++ # If any center is placed such that it cannot even have a minimal positive radius,
++ # it indicates an invalid configuration for which we return zero radii.
++ if np.any(initial_radii_from_walls < tolerance / 2):
++ return np.zeros(n)
++
++ radii = np.copy(initial_radii_from_walls)
++
++ # Step 2: Iteratively adjust radii to resolve overlaps and maximize their sum.
++ # In each iteration, each circle tries to grow as much as possible, limited by walls
++ # and the *current* radii of other circles. This is a fixed-point iteration approach.
++ for iteration in range(max_iterations):
++ something_changed = False
++ new_radii = np.copy(radii) # Calculate updates based on current radii, then apply
++
++ # Iterate through each circle
++ for i in range(n):
++ current_r_i = radii[i]
++
++ # Max possible radius for circle i, constrained by walls
++ max_r_i_walls = initial_radii_from_walls[i]
++
++ # Max possible radius for circle i, constrained by other circles
++ max_r_i_others = float('inf')
++ for j in range(n):
++ if i == j:
++ continue
++ dist_ij = np.linalg.norm(centers[i] - centers[j])
++ # The radius of circle i cannot be larger than dist_ij - radii[j] to avoid overlap
++ # If radii[j] is too large, it might force r_i to be negative, meaning overlap.
++ max_r_i_others = min(max_r_i_others, dist_ij - radii[j])
++
++ # The actual radius of circle i is limited by the minimum of all these constraints
++ candidate_r_i = min(max_r_i_walls, max_r_i_others)
++
++ # A valid radius must be non-negative. If it's negative, it implies an unavoidable overlap
++ # with current other radii, so we clamp it to zero.
++ new_radii[i] = max(0.0, candidate_r_i)
++
++ if abs(new_radii[i] - current_r_i) > tolerance:
++ something_changed = True
++
++ radii = new_radii # Apply all updates simultaneously for this iteration
++
++ if not something_changed:
++ break # Converged
++
++ return radii
+
+ def construct_packing():
+ """
+- Construct a specific arrangement of 26 circles in a unit square
+- that attempts to maximize the sum of their radii.
++ Construct an arrangement of 26 circles in a unit square using Simulated Annealing
++ to maximize the sum of their radii.
+
+ Returns:
+- Tuple of (centers, radii, sum_of_radii)
++ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+- sum_of_radii: Sum of all radii
+ """
+- # Initialize arrays for 26 circles
+ n = 26
+- centers = np.zeros((n, 2))
+
+- # Place circles in a structured pattern
+- # This is a simple pattern - evolution will improve this
++ # --- Simulated Annealing Parameters ---
++ initial_temperature = 1.0 # Starting temperature
++ cooling_rate = 0.999 # Geometric cooling schedule
++ min_temperature = 1e-5 # Stop criterion for temperature
++ iterations_per_temp_step = 2000 # Number of perturbations at each temperature step
++ perturbation_scale = 0.02 # Max displacement for a circle center in a perturbation
++
++ # --- Initial State Generation ---
++ # Attempt a more structured initial placement (e.g., a grid) to provide a better starting point
++ grid_side = int(np.ceil(np.sqrt(n)))
++ centers_initial = np.zeros((n, 2))
++ idx = 0
++ # Calculate step size to distribute circles roughly evenly, plus a margin
++ step_x = 1.0 / (grid_side + 1)
++ step_y = 1.0 / (grid_side + 1)
+
+- # First, place a large circle in the center
+- centers[0] = [0.5, 0.5]
++ for i in range(grid_side):
++ for j in range(grid_side):
++ if idx < n:
++ # Place center with slight random offset to break symmetry
++ centers_initial[idx, 0] = (i + 0.5) * step_x + np.random.uniform(-0.01, 0.01)
++ centers_initial[idx, 1] = (j + 0.5) * step_y + np.random.uniform(-0.01, 0.01)
++ idx += 1
++ # For any remaining circles if n > grid_side*grid_side, place them randomly
++ while idx < n:
++ centers_initial[idx, 0] = np.random.uniform(0.1, 0.9)
++ centers_initial[idx, 1] = np.random.uniform(0.1, 0.9)
++ idx += 1
++
++ # Ensure all initial centers are well within bounds to allow positive radii
++ centers_initial = np.clip(centers_initial, 0.001 + perturbation_scale, 0.999 - perturbation_scale)
+
+- # Place 8 circles around it in a ring
+- for i in range(8):
+- angle = 2 * np.pi * i / 8
+- centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
++ current_centers = centers_initial
++ current_radii = compute_max_radii(current_centers)
++ current_sum_radii = np.sum(current_radii)
+
+- # Place 16 more circles in an outer ring
+- for i in range(16):
+- angle = 2 * np.pi * i / 16
+- centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
++ best_centers = np.copy(current_centers)
++ best_radii = np.copy(current_radii)
++ best_sum_radii = current_sum_radii
+
+- # Additional positioning adjustment to make sure all circles
+- # are inside the square and don't overlap
+- # Clip to ensure everything is inside the unit square
+- centers = np.clip(centers, 0.01, 0.99)
++ temperature = initial_temperature
+
+- # Compute maximum valid radii for this configuration
+- radii = compute_max_radii(centers)
+- return centers, radii
++ # --- Simulated Annealing Loop ---
++ while temperature > min_temperature:
++ for _ in range(iterations_per_temp_step):
++ # Propose a new state by perturbing one circle's center
++ neighbor_centers = np.copy(current_centers)
++
++ # Select a random circle to perturb
++ circle_idx = np.random.randint(n)
++
++ # Generate random displacement
++ dx, dy = np.random.uniform(-perturbation_scale, perturbation_scale, 2)
++
++ # Apply perturbation, clip to stay within square boundaries (with a small margin)
++ neighbor_centers[circle_idx, 0] = np.clip(current_centers[circle_idx, 0] + dx, 0.001, 0.999)
++ neighbor_centers[circle_idx, 1] = np.clip(current_centers[circle_idx, 1] + dy, 0.001, 0.999)
+
++ neighbor_radii = compute_max_radii(neighbor_centers)
++ neighbor_sum_radii = np.sum(neighbor_radii)
+
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
++ # Decision to accept the new state based on Metropolis criterion
++ # Since we want to MAXIMIZE sum_radii, energy is -sum_radii.
++ # Delta_energy = E_new - E_old = (-neighbor_sum_radii) - (-current_sum_radii) = current_sum_radii - neighbor_sum_radii
++
++ if neighbor_sum_radii > current_sum_radii:
++ accept_prob = 1.0 # Always accept better solutions
++ else:
++ # Accept worse solutions with a probability that decreases with temperature
++ delta_energy = current_sum_radii - neighbor_sum_radii
++ # Avoid division by zero if temp drops too low, effectively preventing worse moves
++ if temperature <= 0:
++ accept_prob = 0.0
++ else:
++ accept_prob = np.exp(-delta_energy / temperature)
++
++ if np.random.rand() < accept_prob:
++ current_centers = neighbor_centers
++ current_radii = neighbor_radii
++ current_sum_radii = neighbor_sum_radii
+
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
++ # Update the best state found so far
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_centers = np.copy(current_centers)
++ best_radii = np.copy(current_radii)
+
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
++ temperature *= cooling_rate # Cool down the temperature
+
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
++ # Final evaluation of the best configuration found (optional, but ensures best_radii is accurate)
++ # The radii could be recalculated with higher precision if desired, but `compute_max_radii` is robust.
++ final_radii = compute_max_radii(best_centers)
++ # Recalculate sum_radii to be consistent with final_radii, important if compute_max_radii has slight variations
++ final_sum_radii = np.sum(final_radii)
+
+- # Then, limit by distance to other circles
+- # Each pair of circles with centers at distance d can have
+- # sum of radii at most d to avoid overlap
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+-
+- # If current radii would cause overlap
+- if radii[i] + radii[j] > dist:
+- # Scale both radii proportionally
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- return radii
+-
++ return best_centers, final_radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..38a664fb62ffd4f2fbf51653c8811a042ea39959
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/main.py
@@ -0,0 +1,198 @@
+# EVOLVE-BLOCK-START
+
+def compute_max_radii(centers, max_iterations=50, tolerance=1e-7):
+ """
+ Computes radii for a given set of fixed circle centers such that:
+ 1. All circles are within the unit square.
+ 2. No circles overlap.
+ 3. The sum of radii is locally maximized (greedy growth heuristic).
+
+ This function is used as the fitness evaluator within the Simulated Annealing process.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates of circle centers.
+ max_iterations: Maximum number of iterations for the greedy growth process.
+ A higher value leads to more accurate radii but slower evaluation.
+ tolerance: Convergence tolerance for radii changes. If changes are smaller than this,
+ the iteration stops.
+
+ Returns:
+ np.array of shape (n) with radius of each circle. Returns zeros if any center
+ is outside the unit square such that a valid positive radius is impossible.
+ """
+ n = centers.shape[0]
+
+ # Step 1: Initialize radii based on wall constraints.
+ # Each radius is limited by the distance to the closest wall.
+ # This also acts as an initial check for centers being validly placed within the square.
+ initial_radii_from_walls = np.array([
+ min(c[0], 1 - c[0], c[1], 1 - c[1]) for c in centers
+ ])
+
+ # If any center is placed such that it cannot even have a minimal positive radius,
+ # it indicates an invalid configuration for which we return zero radii.
+ if np.any(initial_radii_from_walls < tolerance / 2):
+ return np.zeros(n)
+
+ radii = np.copy(initial_radii_from_walls)
+
+ # Step 2: Iteratively adjust radii to resolve overlaps and maximize their sum.
+ # In each iteration, each circle tries to grow as much as possible, limited by walls
+ # and the *current* radii of other circles. This is a fixed-point iteration approach.
+ for iteration in range(max_iterations):
+ something_changed = False
+ new_radii = np.copy(radii) # Calculate updates based on current radii, then apply
+
+ # Iterate through each circle
+ for i in range(n):
+ current_r_i = radii[i]
+
+ # Max possible radius for circle i, constrained by walls
+ max_r_i_walls = initial_radii_from_walls[i]
+
+ # Max possible radius for circle i, constrained by other circles
+ max_r_i_others = float('inf')
+ for j in range(n):
+ if i == j:
+ continue
+ dist_ij = np.linalg.norm(centers[i] - centers[j])
+ # The radius of circle i cannot be larger than dist_ij - radii[j] to avoid overlap
+ # If radii[j] is too large, it might force r_i to be negative, meaning overlap.
+ max_r_i_others = min(max_r_i_others, dist_ij - radii[j])
+
+ # The actual radius of circle i is limited by the minimum of all these constraints
+ candidate_r_i = min(max_r_i_walls, max_r_i_others)
+
+ # A valid radius must be non-negative. If it's negative, it implies an unavoidable overlap
+ # with current other radii, so we clamp it to zero.
+ new_radii[i] = max(0.0, candidate_r_i)
+
+ if abs(new_radii[i] - current_r_i) > tolerance:
+ something_changed = True
+
+ radii = new_radii # Apply all updates simultaneously for this iteration
+
+ if not something_changed:
+ break # Converged
+
+ return radii
+
+def construct_packing():
+ """
+ Construct an arrangement of 26 circles in a unit square using Simulated Annealing
+ to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Simulated Annealing Parameters ---
+ initial_temperature = 1.0 # Starting temperature
+ cooling_rate = 0.999 # Geometric cooling schedule
+ min_temperature = 1e-5 # Stop criterion for temperature
+ iterations_per_temp_step = 2000 # Number of perturbations at each temperature step
+ perturbation_scale = 0.02 # Max displacement for a circle center in a perturbation
+
+ # --- Initial State Generation ---
+ # Attempt a more structured initial placement (e.g., a grid) to provide a better starting point
+ grid_side = int(np.ceil(np.sqrt(n)))
+ centers_initial = np.zeros((n, 2))
+ idx = 0
+ # Calculate step size to distribute circles roughly evenly, plus a margin
+ step_x = 1.0 / (grid_side + 1)
+ step_y = 1.0 / (grid_side + 1)
+
+ for i in range(grid_side):
+ for j in range(grid_side):
+ if idx < n:
+ # Place center with slight random offset to break symmetry
+ centers_initial[idx, 0] = (i + 0.5) * step_x + np.random.uniform(-0.01, 0.01)
+ centers_initial[idx, 1] = (j + 0.5) * step_y + np.random.uniform(-0.01, 0.01)
+ idx += 1
+ # For any remaining circles if n > grid_side*grid_side, place them randomly
+ while idx < n:
+ centers_initial[idx, 0] = np.random.uniform(0.1, 0.9)
+ centers_initial[idx, 1] = np.random.uniform(0.1, 0.9)
+ idx += 1
+
+ # Ensure all initial centers are well within bounds to allow positive radii
+ centers_initial = np.clip(centers_initial, 0.001 + perturbation_scale, 0.999 - perturbation_scale)
+
+ current_centers = centers_initial
+ current_radii = compute_max_radii(current_centers)
+ current_sum_radii = np.sum(current_radii)
+
+ best_centers = np.copy(current_centers)
+ best_radii = np.copy(current_radii)
+ best_sum_radii = current_sum_radii
+
+ temperature = initial_temperature
+
+ # --- Simulated Annealing Loop ---
+ while temperature > min_temperature:
+ for _ in range(iterations_per_temp_step):
+ # Propose a new state by perturbing one circle's center
+ neighbor_centers = np.copy(current_centers)
+
+ # Select a random circle to perturb
+ circle_idx = np.random.randint(n)
+
+ # Generate random displacement
+ dx, dy = np.random.uniform(-perturbation_scale, perturbation_scale, 2)
+
+ # Apply perturbation, clip to stay within square boundaries (with a small margin)
+ neighbor_centers[circle_idx, 0] = np.clip(current_centers[circle_idx, 0] + dx, 0.001, 0.999)
+ neighbor_centers[circle_idx, 1] = np.clip(current_centers[circle_idx, 1] + dy, 0.001, 0.999)
+
+ neighbor_radii = compute_max_radii(neighbor_centers)
+ neighbor_sum_radii = np.sum(neighbor_radii)
+
+ # Decision to accept the new state based on Metropolis criterion
+ # Since we want to MAXIMIZE sum_radii, energy is -sum_radii.
+ # Delta_energy = E_new - E_old = (-neighbor_sum_radii) - (-current_sum_radii) = current_sum_radii - neighbor_sum_radii
+
+ if neighbor_sum_radii > current_sum_radii:
+ accept_prob = 1.0 # Always accept better solutions
+ else:
+ # Accept worse solutions with a probability that decreases with temperature
+ delta_energy = current_sum_radii - neighbor_sum_radii
+ # Avoid division by zero if temp drops too low, effectively preventing worse moves
+ if temperature <= 0:
+ accept_prob = 0.0
+ else:
+ accept_prob = np.exp(-delta_energy / temperature)
+
+ if np.random.rand() < accept_prob:
+ current_centers = neighbor_centers
+ current_radii = neighbor_radii
+ current_sum_radii = neighbor_sum_radii
+
+ # Update the best state found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = np.copy(current_centers)
+ best_radii = np.copy(current_radii)
+
+ temperature *= cooling_rate # Cool down the temperature
+
+ # Final evaluation of the best configuration found (optional, but ensures best_radii is accurate)
+ # The radii could be recalculated with higher precision if desired, but `compute_max_radii` is robust.
+ final_radii = compute_max_radii(best_centers)
+ # Recalculate sum_radii to be consistent with final_radii, important if compute_max_radii has slight variations
+ final_sum_radii = np.sum(final_radii)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..9306383d5dbb8671983adb69c529a06d51537b62
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/original.py
@@ -0,0 +1,94 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place circles in a structured pattern
+ # This is a simple pattern - evolution will improve this
+
+ # First, place a large circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles around it in a ring
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
+
+ # Place 16 more circles in an outer ring
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
+
+ # Additional positioning adjustment to make sure all circles
+ # are inside the square and don't overlap
+ # Clip to ensure everything is inside the unit square
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..532a184ac05b691e218af70a296a60241c8442c8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": false,
+ "error": "name 'np' is not defined"
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..39b4c6b348d2600261d8ffe4ebdef7775cd959db
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/job_log.out
@@ -0,0 +1,14 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results
+Evaluation error: name 'np' is not defined
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/metrics.json
+Evaluation or Validation failed: name 'np' is not defined
+Metrics:
+ combined_score: 0.0
+ execution_time_mean: 0.0
+ execution_time_std: 0.0
+ num_successful_runs: 0
+ num_valid_runs: 0
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..fec2021a6a036637c9d3049ae9778c77cef78421
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/results/metrics.json
@@ -0,0 +1,9 @@
+{
+ "combined_score": 0.0,
+ "execution_time_mean": 0.0,
+ "execution_time_std": 0.0,
+ "num_successful_runs": 0,
+ "num_valid_runs": 0,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5078ea3528b91c5f700a913372561f546910ae58
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_3/rewrite.txt
@@ -0,0 +1,200 @@
+import numpy as np
+
+# EVOLVE-BLOCK-START
+
+def compute_max_radii(centers, max_iterations=50, tolerance=1e-7):
+ """
+ Computes radii for a given set of fixed circle centers such that:
+ 1. All circles are within the unit square.
+ 2. No circles overlap.
+ 3. The sum of radii is locally maximized (greedy growth heuristic).
+
+ This function is used as the fitness evaluator within the Simulated Annealing process.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates of circle centers.
+ max_iterations: Maximum number of iterations for the greedy growth process.
+ A higher value leads to more accurate radii but slower evaluation.
+ tolerance: Convergence tolerance for radii changes. If changes are smaller than this,
+ the iteration stops.
+
+ Returns:
+ np.array of shape (n) with radius of each circle. Returns zeros if any center
+ is outside the unit square such that a valid positive radius is impossible.
+ """
+ n = centers.shape[0]
+
+ # Step 1: Initialize radii based on wall constraints.
+ # Each radius is limited by the distance to the closest wall.
+ # This also acts as an initial check for centers being validly placed within the square.
+ initial_radii_from_walls = np.array([
+ min(c[0], 1 - c[0], c[1], 1 - c[1]) for c in centers
+ ])
+
+ # If any center is placed such that it cannot even have a minimal positive radius,
+ # it indicates an invalid configuration for which we return zero radii.
+ if np.any(initial_radii_from_walls < tolerance / 2):
+ return np.zeros(n)
+
+ radii = np.copy(initial_radii_from_walls)
+
+ # Step 2: Iteratively adjust radii to resolve overlaps and maximize their sum.
+ # In each iteration, each circle tries to grow as much as possible, limited by walls
+ # and the *current* radii of other circles. This is a fixed-point iteration approach.
+ for iteration in range(max_iterations):
+ something_changed = False
+ new_radii = np.copy(radii) # Calculate updates based on current radii, then apply
+
+ # Iterate through each circle
+ for i in range(n):
+ current_r_i = radii[i]
+
+ # Max possible radius for circle i, constrained by walls
+ max_r_i_walls = initial_radii_from_walls[i]
+
+ # Max possible radius for circle i, constrained by other circles
+ max_r_i_others = float('inf')
+ for j in range(n):
+ if i == j:
+ continue
+ dist_ij = np.linalg.norm(centers[i] - centers[j])
+ # The radius of circle i cannot be larger than dist_ij - radii[j] to avoid overlap
+ # If radii[j] is too large, it might force r_i to be negative, meaning overlap.
+ max_r_i_others = min(max_r_i_others, dist_ij - radii[j])
+
+ # The actual radius of circle i is limited by the minimum of all these constraints
+ candidate_r_i = min(max_r_i_walls, max_r_i_others)
+
+ # A valid radius must be non-negative. If it's negative, it implies an unavoidable overlap
+ # with current other radii, so we clamp it to zero.
+ new_radii[i] = max(0.0, candidate_r_i)
+
+ if abs(new_radii[i] - current_r_i) > tolerance:
+ something_changed = True
+
+ radii = new_radii # Apply all updates simultaneously for this iteration
+
+ if not something_changed:
+ break # Converged
+
+ return radii
+
+def construct_packing():
+ """
+ Construct an arrangement of 26 circles in a unit square using Simulated Annealing
+ to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Simulated Annealing Parameters ---
+ initial_temperature = 1.0 # Starting temperature
+ cooling_rate = 0.999 # Geometric cooling schedule
+ min_temperature = 1e-5 # Stop criterion for temperature
+ iterations_per_temp_step = 2000 # Number of perturbations at each temperature step
+ perturbation_scale = 0.02 # Max displacement for a circle center in a perturbation
+
+ # --- Initial State Generation ---
+ # Attempt a more structured initial placement (e.g., a grid) to provide a better starting point
+ grid_side = int(np.ceil(np.sqrt(n)))
+ centers_initial = np.zeros((n, 2))
+ idx = 0
+ # Calculate step size to distribute circles roughly evenly, plus a margin
+ step_x = 1.0 / (grid_side + 1)
+ step_y = 1.0 / (grid_side + 1)
+
+ for i in range(grid_side):
+ for j in range(grid_side):
+ if idx < n:
+ # Place center with slight random offset to break symmetry
+ centers_initial[idx, 0] = (i + 0.5) * step_x + np.random.uniform(-0.01, 0.01)
+ centers_initial[idx, 1] = (j + 0.5) * step_y + np.random.uniform(-0.01, 0.01)
+ idx += 1
+ # For any remaining circles if n > grid_side*grid_side, place them randomly
+ while idx < n:
+ centers_initial[idx, 0] = np.random.uniform(0.1, 0.9)
+ centers_initial[idx, 1] = np.random.uniform(0.1, 0.9)
+ idx += 1
+
+ # Ensure all initial centers are well within bounds to allow positive radii
+ centers_initial = np.clip(centers_initial, 0.001 + perturbation_scale, 0.999 - perturbation_scale)
+
+ current_centers = centers_initial
+ current_radii = compute_max_radii(current_centers)
+ current_sum_radii = np.sum(current_radii)
+
+ best_centers = np.copy(current_centers)
+ best_radii = np.copy(current_radii)
+ best_sum_radii = current_sum_radii
+
+ temperature = initial_temperature
+
+ # --- Simulated Annealing Loop ---
+ while temperature > min_temperature:
+ for _ in range(iterations_per_temp_step):
+ # Propose a new state by perturbing one circle's center
+ neighbor_centers = np.copy(current_centers)
+
+ # Select a random circle to perturb
+ circle_idx = np.random.randint(n)
+
+ # Generate random displacement
+ dx, dy = np.random.uniform(-perturbation_scale, perturbation_scale, 2)
+
+ # Apply perturbation, clip to stay within square boundaries (with a small margin)
+ neighbor_centers[circle_idx, 0] = np.clip(current_centers[circle_idx, 0] + dx, 0.001, 0.999)
+ neighbor_centers[circle_idx, 1] = np.clip(current_centers[circle_idx, 1] + dy, 0.001, 0.999)
+
+ neighbor_radii = compute_max_radii(neighbor_centers)
+ neighbor_sum_radii = np.sum(neighbor_radii)
+
+ # Decision to accept the new state based on Metropolis criterion
+ # Since we want to MAXIMIZE sum_radii, energy is -sum_radii.
+ # Delta_energy = E_new - E_old = (-neighbor_sum_radii) - (-current_sum_radii) = current_sum_radii - neighbor_sum_radii
+
+ if neighbor_sum_radii > current_sum_radii:
+ accept_prob = 1.0 # Always accept better solutions
+ else:
+ # Accept worse solutions with a probability that decreases with temperature
+ delta_energy = current_sum_radii - neighbor_sum_radii
+ # Avoid division by zero if temp drops too low, effectively preventing worse moves
+ if temperature <= 0:
+ accept_prob = 0.0
+ else:
+ accept_prob = np.exp(-delta_energy / temperature)
+
+ if np.random.rand() < accept_prob:
+ current_centers = neighbor_centers
+ current_radii = neighbor_radii
+ current_sum_radii = neighbor_sum_radii
+
+ # Update the best state found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = np.copy(current_centers)
+ best_radii = np.copy(current_radii)
+
+ temperature *= cooling_rate # Cool down the temperature
+
+ # Final evaluation of the best configuration found (optional, but ensures best_radii is accurate)
+ # The radii could be recalculated with higher precision if desired, but `compute_max_radii` is robust.
+ final_radii = compute_max_radii(best_centers)
+ # Recalculate sum_radii to be consistent with final_radii, important if compute_max_radii has slight variations
+ final_sum_radii = np.sum(final_radii)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..aac1b41cbf091d222fa2a49d514d542bdf238f20
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/edit.diff
@@ -0,0 +1,319 @@
+--- a/original.py
++++ b/original.py
+@@ -1,164 +1,239 @@
+ # EVOLVE-BLOCK-START
+ """
+-Improved physical simulation packing for n=26 circles.
+-This algorithm starts with a more symmetric and promising grid-based
+-configuration and uses an enhanced force model with "growth pressure"
+-to actively seek out more optimal arrangements.
++Physical simulation-based circle packing for n=26 circles.
++This algorithm iteratively refines circle positions to maximize radii sum.
++Improved by combining insights from high-scoring prior programs:
++- Optimized initial placement of the 26th circle.
++- Reduced initial perturbation noise.
++- Tuned learning rate for fine-grained adjustments.
++- Increased iterations for radius calculation convergence.
++- Robust handling of very close circle centers in radius calculation.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles by starting with an interstitial
+- grid layout and then simulating physical forces to find an optimal
+- arrangement of centers.
++ Constructs a packing of 26 circles by simulating physical repulsion forces
++ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- # More iterations to allow the system to settle into a fine-tuned state.
+- iterations = 500
+- # A smaller learning rate is used for careful refinement of a good initial guess.
+- learning_rate = 0.01
++ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
++ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+- # A growth factor to create a "pressure" for circles to expand,
+- # generating repulsive forces even when they are just touching.
+- growth_pressure = 1.005
+-
+- # --- Initialization ---
+- # Start with a 5x5 grid, a proven strong configuration.
+- centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- centers[k, 0] = (i + 0.5) * spacing
+- centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- # Place the 26th circle in the center of a grid cell. This is a
+- # more balanced starting point than a corner, as it doesn't
+- # immediately crowd any single circle. It mimics the known optimal
+- # structure for N=26.
+- centers[25] = [0.2, 0.2]
+-
+- # No random noise is added. We want to deterministically refine this specific strong starting point.
+-
+- # --- Simulation Loop ---
+- for k_iter in range(iterations):
+- # 1. Calculate radii for the current center configuration
+- radii = compute_max_radii(centers)
+-
+- # 2. Calculate forces to push centers to a better configuration
++ initial_growth_pressure = 1.01 # Start with slightly higher pressure to aggressively expand
++ final_growth_pressure = 1.001 # End with a smaller pressure to allow finer adjustments
++
++ best_sum_radii = -1.0
++ best_final_centers = None
++ best_final_radii = None
++
++ # Candidate initial positions for the 26th circle to optimize corner utilization.
++ candidate_extra_circle_initial_positions = [
++ [0.05, 0.05], # Original effective position for corner
++ [0.05, 0.95], # Top-left corner
++ [0.95, 0.05], # Bottom-right corner
++ [0.95, 0.95], # Top-right corner
++ [0.2, 0.2], # Position that yielded 1.92 in a prior program
++ [0.2, 0.8], # Symmetric to [0.2, 0.2]
++ [0.8, 0.2], # Symmetric to [0.2, 0.2]
++ [0.8, 0.8], # Symmetric to [0.2, 0.2]
++ [0.5, 0.5], # Center position
++ ]
++
++ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
++ # --- Initialization for current trial ---
++ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
++ # Re-initialize centers for each trial to ensure independent simulations.
++ current_centers = np.zeros((n, 2))
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for i in range(num_cells_side):
++ for j in range(num_cells_side):
++ current_centers[k, 0] = (i + 0.5) * spacing
++ current_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ # Place the 26th circle at the current candidate position.
++ current_centers[25] = initial_pos_26_th_circle
++
++ # Use a copy of centers for the simulation, as it modifies the array
++ centers_for_sim = current_centers.copy()
++
++ # --- Simulation Loop ---
++ for sim_iter in range(iterations):
++ # 1. Calculate radii for the current center configuration
++ radii = compute_max_radii(centers_for_sim)
++
++ # 2. Calculate forces to push centers to a better configuration
++ forces = np.zeros((n, 2))
++
++ # Apply adaptive growth pressure: anneal from initial_growth_pressure to final_growth_pressure
++ current_growth_pressure = initial_growth_pressure - \
++ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
++ inflated_radii = radii * current_growth_pressure
++
++ # a. Circle-to-circle repulsion forces
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers_for_sim[i] - centers_for_sim[j]
++ dist = np.linalg.norm(vec)
++
++ if dist < 1e-9:
++ continue
++
++ # Use inflated radii for overlap calculation
++ overlap = inflated_radii[i] + inflated_radii[j] - dist
++
++ if overlap > 0:
++ force_magnitude = overlap
++ force_vec = (vec / dist) * force_magnitude
++ forces[i] += force_vec
++ forces[j] -= force_vec
++
++ # b. Wall repulsion forces, also using inflated radii
++ for i in range(n):
++ # Left wall
++ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
++ if overlap_left > 0:
++ forces[i, 0] += wall_repulsion_strength * overlap_left
++ # Right wall
++ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
++ if overlap_right > 0:
++ forces[i, 0] -= wall_repulsion_strength * overlap_right
++ # Bottom wall
++ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
++ if overlap_bottom > 0:
++ forces[i, 1] += wall_repulsion_strength * overlap_bottom
++ # Top wall
++ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
++ if overlap_top > 0:
++ forces[i, 1] -= wall_repulsion_strength * overlap_top
++
++ # 3. Update center positions based on forces
++ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
++ centers_for_sim += forces * current_lr
++
++ # 4. Enforce boundary constraints - keep centers within [0,1]
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # --- Finalization for current trial ---
++ current_final_radii = compute_max_radii(centers_for_sim)
++ current_sum_radii = np.sum(current_final_radii)
++
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_final_centers = centers_for_sim.copy() # Store a copy
++ best_final_radii = current_final_radii.copy() # Store a copy
++
++ # --- Post-Simulation Fine-Tuning Phase ---
++ # Run a few more iterations with a very small learning rate and no growth pressure
++ # to allow centers to settle precisely, without aggressive expansion.
++ fine_tune_iterations = 50
++ fine_tune_learning_rate = 0.001
++
++ centers_for_sim = best_final_centers.copy() # Start fine-tuning from the best configuration
++
++ for _ in range(fine_tune_iterations):
++ radii = compute_max_radii(centers_for_sim)
+ forces = np.zeros((n, 2))
+
+- # Create "inflated" radii to generate repulsive forces even when just touching
+- inflated_radii = radii * growth_pressure
+-
+- # a. Circle-to-circle repulsion forces
++ # a. Circle-to-circle repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ for j in range(i + 1, n):
+- vec = centers[i] - centers[j]
++ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+- # Use inflated radii to calculate overlap, creating a growth pressure
+- overlap = inflated_radii[i] + inflated_radii[j] - dist
++ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+- # b. Wall repulsion forces, also using inflated radii
++ # b. Wall repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ # Left wall
+- overlap = inflated_radii[i] - centers[i, 0]
+- if overlap > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap
++ overlap_left = radii[i] - centers_for_sim[i, 0]
++ if overlap_left > 0:
++ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+- overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+- if overlap > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap
++ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
++ if overlap_right > 0:
++ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+- overlap = inflated_radii[i] - centers[i, 1]
+- if overlap > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap
++ overlap_bottom = radii[i] - centers_for_sim[i, 1]
++ if overlap_bottom > 0:
++ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+- overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+- if overlap > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap
+-
+- # 3. Update center positions based on forces
+- # We adjust the learning rate over time (annealing) to help the system settle
+- current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+- centers += forces * current_lr
+-
+- # 4. Enforce boundary constraints
+- centers = np.clip(centers, 0.0, 1.0)
+-
+- # --- Finalization ---
+- # After the simulation, do a final radius calculation on the optimized centers
+- final_centers = centers
++ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
++ if overlap_top > 0:
++ forces[i, 1] -= wall_repulsion_strength * overlap_top
++
++ centers_for_sim += forces * fine_tune_learning_rate
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # After fine-tuning, perform a final radius calculation
++ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+-
+- return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence.
+- for _ in range(250):
++ # Increased iterations for better convergence (aligning with 1.93 program's 500 iterations).
++ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..d514a86bc169252bec11493e53c99b41b461528c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/main.py
@@ -0,0 +1,239 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01 # Start with slightly higher pressure to aggressively expand
+ final_growth_pressure = 1.001 # End with a smaller pressure to allow finer adjustments
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position for corner
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ [0.5, 0.5], # Center position
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26_th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Apply adaptive growth pressure: anneal from initial_growth_pressure to final_growth_pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Use inflated radii for overlap calculation
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Run a few more iterations with a very small learning rate and no growth pressure
+ # to allow centers to settle precisely, without aggressive expansion.
+ fine_tune_iterations = 50
+ fine_tune_learning_rate = 0.001
+
+ centers_for_sim = best_final_centers.copy() # Start fine-tuning from the best configuration
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After fine-tuning, perform a final radius calculation
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (aligning with 1.93 program's 500 iterations).
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..4025a0eb85fb36c6bdf2411aa21c60171c4e402f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/original.py
@@ -0,0 +1,164 @@
+# EVOLVE-BLOCK-START
+"""
+Improved physical simulation packing for n=26 circles.
+This algorithm starts with a more symmetric and promising grid-based
+configuration and uses an enhanced force model with "growth pressure"
+to actively seek out more optimal arrangements.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by starting with an interstitial
+ grid layout and then simulating physical forces to find an optimal
+ arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # More iterations to allow the system to settle into a fine-tuned state.
+ iterations = 500
+ # A smaller learning rate is used for careful refinement of a good initial guess.
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ # A growth factor to create a "pressure" for circles to expand,
+ # generating repulsive forces even when they are just touching.
+ growth_pressure = 1.005
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, a proven strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the center of a grid cell. This is a
+ # more balanced starting point than a corner, as it doesn't
+ # immediately crowd any single circle. It mimics the known optimal
+ # structure for N=26.
+ centers[25] = [0.2, 0.2]
+
+ # No random noise is added. We want to deterministically refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Use inflated radii to calculate overlap, creating a growth pressure
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap = inflated_radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = inflated_radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fab8379dc3cf221a8ab9a15d99bf1ee78ccdc12f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_30/search_replace.txt
@@ -0,0 +1,332 @@
+
+
+This modification aims to replicate the most successful strategy found in a previous run, which achieved a score of 1.93 (compared to the current 1.92). The strategy involves several key enhancements:
+
+1. **Multiple Initial Positions for the 26th Circle**: Instead of fixing the 26th circle's initial position to `[0.2, 0.2]`, the algorithm now iterates through a set of promising candidate positions (including corners, edges, and the center). For each candidate, it runs the full simulation and keeps track of the configuration that yields the highest sum of radii. This dynamic selection significantly improves the chances of finding a better global optimum.
+
+2. **Annealed Growth Pressure**: The `growth_pressure` parameter, which encourages circles to expand and push apart, is no longer fixed. It now anneals from a higher `initial_growth_pressure` (1.01) to a slightly lower `final_growth_pressure` (1.001) over the course of the simulation. This allows for more aggressive expansion and overlap resolution early on, followed by finer adjustments as the system settles.
+
+3. **Post-Simulation Fine-Tuning Phase**: After the main simulation loop concludes for the best configuration, a short, dedicated fine-tuning phase is introduced. This phase uses a very small, constant learning rate (`0.001`) and removes the `growth_pressure` influence, allowing the circles to settle into their final positions with minimal aggressive forces, potentially escaping shallow local minima.
+
+These combined changes directly integrate proven optimization techniques from a higher-performing prior generation, expected to lead to a significant improvement in the sum of radii.
+
+
+<<<<<<< SEARCH
+"""
+Improved physical simulation packing for n=26 circles.
+This algorithm starts with a more symmetric and promising grid-based
+configuration and uses an enhanced force model with "growth pressure"
+to actively seek out more optimal arrangements.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by starting with an interstitial
+ grid layout and then simulating physical forces to find an optimal
+ arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # More iterations to allow the system to settle into a fine-tuned state.
+ iterations = 500
+ # A smaller learning rate is used for careful refinement of a good initial guess.
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ # A growth factor to create a "pressure" for circles to expand,
+ # generating repulsive forces even when they are just touching.
+ growth_pressure = 1.005
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, a proven strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the center of a grid cell. This is a
+ # more balanced starting point than a corner, as it doesn't
+ # immediately crowd any single circle. It mimics the known optimal
+ # structure for N=26.
+ centers[25] = [0.2, 0.2]
+
+ # No random noise is added. We want to deterministically refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Use inflated radii to calculate overlap, creating a growth pressure
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap = inflated_radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = inflated_radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+=======
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01 # Start with slightly higher pressure to aggressively expand
+ final_growth_pressure = 1.001 # End with a smaller pressure to allow finer adjustments
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position for corner
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ [0.5, 0.5], # Center position
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26_th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Apply adaptive growth pressure: anneal from initial_growth_pressure to final_growth_pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Use inflated radii for overlap calculation
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Run a few more iterations with a very small learning rate and no growth pressure
+ # to allow centers to settle precisely, without aggressive expansion.
+ fine_tune_iterations = 50
+ fine_tune_learning_rate = 0.001
+
+ centers_for_sim = best_final_centers.copy() # Start fine-tuning from the best configuration
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces (using actual radii, no growth pressure)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After fine-tuning, perform a final radius calculation
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+>>>>>>> REPLACE
+
+
+
+
+This change increases the number of iterations in the `compute_max_radii` function from 250 to 500. The `compute_max_radii` function uses an iterative proportional scaling method to determine the largest possible radii for all circles without overlap and within the square boundaries. A higher number of iterations allows this iterative process to converge more fully, leading to more accurate and potentially larger radii values, which directly contributes to a higher sum of radii. This aligns with the observation that prior programs achieving higher scores (e.g., the 1.93 program) explicitly used 500 iterations for this critical step.
+
+
+<<<<<<< SEARCH
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence.
+ for _ in range(250):
+=======
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (aligning with 1.93 program's 500 iterations).
+ for _ in range(500):
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f632d49368d2fe47dcd3c7578abca06b06f7100b
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..e53b04d78f36114b480da75786b92278f69cbc7c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/edit.diff
@@ -0,0 +1,219 @@
+--- a/original.py
++++ b/original.py
+@@ -1,201 +1,206 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # More simulation steps for better convergence
+- initial_learning_rate = 0.05 # Slightly more aggressive initial movement
+- wall_repulsion_strength = 0.5 # Kept from prior working simulations
++ initial_learning_rate = 0.02 # Tuned for stability and better convergence (from 0.05)
++ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space (from 0.5)
++ growth_pressure_factor = 1.005 # Reintroduced and tuned expansion pressure to fill gaps
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+- # 1. Calculate radii for the current center configuration
+- radii_for_sim = compute_max_radii(centers_for_sim)
+-
+- # 2. Calculate forces to push centers to a better configuration
++ # 1. Calculate the maximum non-overlapping radii for the current positions.
++ current_radii = compute_max_radii(centers_for_sim)
++
++ # 2. Artificially inflate radii to create an expansion 'pressure'.
++ pressured_radii = current_radii * growth_pressure_factor
++
++ # 3. Calculate forces to push centers to a better configuration.
+ forces = np.zeros((n, 2))
+
+- # a. Circle-to-circle repulsion forces
++ # a. Circle-to-circle repulsion forces based on artificial overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+- continue # No force calculation needed, radii_for_sim handles overlap
+-
+- overlap = radii_for_sim[i] + radii_for_sim[j] - dist
++ continue
++
++ # Calculate overlap using the inflated, 'pressured' radii
++ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute final radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # If the loop somehow failed to find any valid configuration
+ # This fallback needs to be reconsidered if simulation can lead to all radii being zero
+ # For now, it will use the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..5df81345ac341692d3249547279a72b32c148659
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/main.py
@@ -0,0 +1,206 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # More simulation steps for better convergence
+ initial_learning_rate = 0.02 # Tuned for stability and better convergence (from 0.05)
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space (from 0.5)
+ growth_pressure_factor = 1.005 # Reintroduced and tuned expansion pressure to fill gaps
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = compute_max_radii(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * growth_pressure_factor
+
+ # 3. Calculate forces to push centers to a better configuration.
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces based on artificial overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+ continue
+
+ # Calculate overlap using the inflated, 'pressured' radii
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute final radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # If the loop somehow failed to find any valid configuration
+ # This fallback needs to be reconsidered if simulation can lead to all radii being zero
+ # For now, it will use the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..59273f5344ff344d766f71645eebc3282e2764ce
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/original.py
@@ -0,0 +1,201 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Refined Initial Placement Strategy ---
+ # Arrange 25 circles in a 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # These are interstitial points and the center of the square.
+ # The interstitial points are derived from the 5x5 grid's structure.
+ # E.g., for spacing 0.2, centers are 0.1, 0.3, etc. Interstitial points are 0.2, 0.4, etc.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing] # (0.8, 0.8)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # More simulation steps for better convergence
+ initial_learning_rate = 0.05 # Slightly more aggressive initial movement
+ wall_repulsion_strength = 0.5 # Kept from prior working simulations
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current center configuration
+ radii_for_sim = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+ continue # No force calculation needed, radii_for_sim handles overlap
+
+ overlap = radii_for_sim[i] + radii_for_sim[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute final radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # If the loop somehow failed to find any valid configuration
+ # This fallback needs to be reconsidered if simulation can lead to all radii being zero
+ # For now, it will use the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(500): # Increased iterations to 500 for better convergence
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # For packing, it's safer to consider them as overlapping severely and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..9940bd217c9f9ae761c7a1939eaeec94bfce7c40
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": false,
+ "error": "name 'radii_for_sim' is not defined"
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..cc08280cb18557f9ab870bc1489c23605afeb3c8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/job_log.out
@@ -0,0 +1,14 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results
+Evaluation error: name 'radii_for_sim' is not defined
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/metrics.json
+Evaluation or Validation failed: name 'radii_for_sim' is not defined
+Metrics:
+ combined_score: 0.0
+ execution_time_mean: 0.0
+ execution_time_std: 0.0
+ num_successful_runs: 0
+ num_valid_runs: 0
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..fec2021a6a036637c9d3049ae9778c77cef78421
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/results/metrics.json
@@ -0,0 +1,9 @@
+{
+ "combined_score": 0.0,
+ "execution_time_mean": 0.0,
+ "execution_time_std": 0.0,
+ "num_successful_runs": 0,
+ "num_valid_runs": 0,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4d014828cd11e95691f59d98bb127a9e32a6fa87
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_31/search_replace.txt
@@ -0,0 +1,91 @@
+
+The current force-directed simulation in `construct_packing` lacks the `growth_pressure` mechanism, which was a key component of higher-scoring prior programs (like the `ConfigurableHybridPacker` which achieved 1.93). Reintroducing this parameter, along with fine-tuning the `initial_learning_rate` and `wall_repulsion_strength` to match values from successful prior implementations, should significantly improve the packing density. `growth_pressure_factor` acts as a continuous expansion force, helping circles to "squeeze" into tighter configurations and escape local minima. A slightly lower `learning_rate` (0.02 instead of 0.05) combined with a higher `wall_strength` (0.8 instead of 0.5) from the 1.93-scoring program should lead to more stable convergence and better boundary utilization.
+
+
+
+
+<<<<<<< SEARCH
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # More simulation steps for better convergence
+ initial_learning_rate = 0.05 # Slightly more aggressive initial movement
+ wall_repulsion_strength = 0.5 # Kept from prior working simulations
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current center configuration
+ radii_for_sim = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+ continue # No force calculation needed, radii_for_sim handles overlap
+
+ overlap = radii_for_sim[i] + radii_for_sim[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+=======
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 500 # More simulation steps for better convergence
+ initial_learning_rate = 0.02 # Tuned for stability and better convergence (from 0.05)
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space (from 0.5)
+ growth_pressure_factor = 1.005 # Reintroduced and tuned expansion pressure to fill gaps
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = compute_max_radii(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * growth_pressure_factor
+
+ # 3. Calculate forces to push centers to a better configuration.
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces based on artificial overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close, effectively identical
+ continue
+
+ # Calculate overlap using the inflated, 'pressured' radii
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..72d651b9fce3017e76a7c5f4a2f05f67bfec5d1c
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..80d4d2c7c96f56fee6073d28170811de7ad2ca36
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/edit.diff
@@ -0,0 +1,225 @@
+--- a/original.py
++++ b/original.py
+@@ -1,152 +1,216 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+- growth_pressure = self.config['growth_pressure']
++ initial_growth_pressure = self.config['initial_growth_pressure']
++ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
++ # Anneal growth pressure quadratically from high to low.
++ # This 'melts' the initial configuration and then lets it 'recrystallize'.
++ progress = i_iter / iterations
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+- pressured_radii = current_radii * growth_pressure
++ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ vec = self.centers[i] - self.centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion
+ for i in range(self.n):
+ overlap_l = pressured_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
++ def _run_fine_tuning(self):
++ """
++ Performs a final, short simulation with no growth pressure. This allows
++ the packing to settle and resolve any minor residual overlaps from the
++ main simulation, enabling a more precise final state.
++ """
++ iterations = self.config['fine_tune_iter']
++ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
++ wall_strength = self.config['wall_strength']
++
++ for _ in range(iterations):
++ # 1. Calculate radii for the current positions. NO growth pressure.
++ current_radii = self._compute_radii_for_centers(self.centers)
++
++ forces = np.zeros_like(self.centers)
++
++ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
++ # a) Circle-to-circle repulsion
++ for i in range(self.n):
++ for j in range(i + 1, self.n):
++ vec = self.centers[i] - self.centers[j]
++ dist = np.linalg.norm(vec)
++ if dist < 1e-9: continue
++
++ # Overlap is based on actual radii.
++ overlap = current_radii[i] + current_radii[j] - dist
++ if overlap > 0:
++ force_magnitude = overlap
++ force_vec = (vec / dist) * force_magnitude
++ forces[i] += force_vec
++ forces[j] -= force_vec
++
++ # b) Wall repulsion (based on actual radii)
++ for i in range(self.n):
++ overlap_l = current_radii[i] - self.centers[i, 0]
++ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
++
++ overlap_r = (self.centers[i, 0] + current_radii[i]) - 1.0
++ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
++
++ overlap_b = current_radii[i] - self.centers[i, 1]
++ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
++
++ overlap_t = (self.centers[i, 1] + current_radii[i]) - 1.0
++ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
++
++ # 3. Update positions with small learning rate.
++ self.centers += forces * lr
++
++ # 4. Enforce hard boundary constraints.
++ self.centers = np.clip(self.centers, 0.0, 1.0)
++
+ def pack(self):
+- """Executes the full packing pipeline: initialize, refine, and finalize."""
++ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
++ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+ def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+- # Hyperparameters are now centralized in a configuration dictionary,
+- # making the packer's behavior explicit and easier to tune.
++ # Hyperparameters are now centralized in a configuration dictionary.
++ # We introduce an annealing schedule for growth pressure to break out of
++ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+ 'sim_iter': 500, # More iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+- 'growth_pressure': 1.005 # Gentle expansion pressure to fill gaps.
++ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
++ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
++ 'fine_tune_iter': 100, # Iterations for final settlement without growth pressure.
++ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..60b8484c14953d1706ea5db2b33fbefa3d6e837d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/main.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ vec = self.centers[i] - self.centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion
+ for i in range(self.n):
+ overlap_l = pressured_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ vec = self.centers[i] - self.centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Overlap is based on actual radii.
+ overlap = current_radii[i] + current_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(self.n):
+ overlap_l = current_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = current_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary.
+ # We introduce an annealing schedule for growth pressure to break out of
+ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+ 'sim_iter': 500, # More iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 100, # Iterations for final settlement without growth pressure.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb85fe68fe11477c44278bd1e32de5430a77c481
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/original.py
@@ -0,0 +1,152 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ growth_pressure = self.config['growth_pressure']
+
+ for i_iter in range(iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ vec = self.centers[i] - self.centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion
+ for i in range(self.n):
+ overlap_l = pressured_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary,
+ # making the packer's behavior explicit and easier to tune.
+ packer_config = {
+ 'sim_iter': 500, # More iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'growth_pressure': 1.005 # Gentle expansion pressure to fill gaps.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..f7e1f404062cb6ca02af28fbf5551ce8ac27035f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results
+Run 1/1 completed in 2.29 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9372087113372192
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8991, 0.1009)\n centers[5] = (0.0957, 0.3043)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.4949, 0.7001)\n centers[18] = (0.6980, 0.6938)\n centers[19] = (0.9053, 0.6999)\n centers[20] = (0.0973, 0.8995)\n centers[21] = (0.2976, 0.8971)\n centers[22] = (0.5030, 0.8971)\n centers[23] = (0.7024, 0.9035)\n centers[24] = (0.8997, 0.8988)\n centers[25] = (0.2043, 0.1957)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9372087113372192}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/packing_viz.png
+ execution_time_mean: 2.2889661458320916
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..b14a9e48f2000c199a02c2ef20e280a9003c7c63
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9372087113372192,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8991, 0.1009)\n centers[5] = (0.0957, 0.3043)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.4949, 0.7001)\n centers[18] = (0.6980, 0.6938)\n centers[19] = (0.9053, 0.6999)\n centers[20] = (0.0973, 0.8995)\n centers[21] = (0.2976, 0.8971)\n centers[22] = (0.5030, 0.8971)\n centers[23] = (0.7024, 0.9035)\n centers[24] = (0.8997, 0.8988)\n centers[25] = (0.2043, 0.1957)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9372087113372192
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/results/packing_viz.png",
+ "execution_time_mean": 2.2889661458320916,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e915bd958a020947a6a56ac50539c19bea9fd935
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_32/search_replace.txt
@@ -0,0 +1,154 @@
+
+annealing_pressure_and_fine_tuning
+
+
+This change introduces a more sophisticated two-stage optimization process to significantly improve the search for denser packings and escape the local minimum of the initial grid configuration.
+
+1. **Annealed Growth Pressure:** The main simulation's constant `growth_pressure` is replaced with a quadratically annealing pressure. It starts high (`1.02`) to "melt" the rigid 5x5 grid structure, forcing circles to explore more complex arrangements. It then gradually decreases to a low value (`1.001`), allowing the system to "recrystallize" into a new, more optimal configuration. This simulated annealing approach is a standard and powerful technique for escaping local optima in complex search spaces.
+
+2. **Fine-Tuning Phase:** A new, short simulation phase is added after the main refinement loop. This "fine-tuning" phase runs for 100 iterations with a very small, constant learning rate (`0.001`) and, crucially, **no growth pressure**. Its sole purpose is to act as a relaxation step, resolving any minor residual overlaps from the main simulation without any artificial expansion forces. This allows the packing to settle into a more precise and stable final state.
+
+This combined strategy of a high-energy exploration phase followed by a low-energy refinement and settling phase is designed to dramatically improve the packing quality over the previous simple refinement.
+
+
+<<<<<<< SEARCH
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary,
+ # making the packer's behavior explicit and easier to tune.
+ packer_config = {
+ 'sim_iter': 500, # More iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'growth_pressure': 1.005 # Gentle expansion pressure to fill gaps.
+ }
+=======
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ vec = self.centers[i] - self.centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Overlap is based on actual radii.
+ overlap = current_radii[i] + current_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(self.n):
+ overlap_l = current_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = current_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary.
+ # We introduce an annealing schedule for growth pressure to break out of
+ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+ 'sim_iter': 500, # More iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 100, # Iterations for final settlement without growth pressure.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ growth_pressure = self.config['growth_pressure']
+
+ for i_iter in range(iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * growth_pressure
+=======
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..dda85f5569de2ce6bca75b79fba6acf216e91955
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..23487c994bc218f2bba05c5128584e9b033565c2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/edit.diff
@@ -0,0 +1,208 @@
+--- a/original.py
++++ b/original.py
+@@ -1,199 +1,199 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ This version is a crossover, combining the high-level strategy of the best-performing
+ parent (multi-start, two-phase simulation, annealed growth pressure) with the
+ efficient vectorized force calculations from the inspiration program.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- # Increased iterations due to vectorized efficiency
+- iterations = 750
++ # Longer simulation with adjusted forces to find better minima.
++ iterations = 1000
+ learning_rate = 0.01
+- wall_repulsion_strength = 0.5
+- initial_growth_pressure = 1.01
+- final_growth_pressure = 1.001
++ wall_repulsion_strength = 0.25 # Softer walls to encourage boundary packing
++ initial_growth_pressure = 1.015 # Higher initial pressure for stronger exploration
++ final_growth_pressure = 1.0005 # Lower final pressure for smoother transition to fine-tuning
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 100 # Increased due to efficiency
++ fine_tune_iterations = 250 # More iterations for precise settling
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c9de1a96e46a8a927c680f45ed56ac1decd7429
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/main.py
@@ -0,0 +1,199 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Longer simulation with adjusted forces to find better minima.
+ iterations = 1000
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.25 # Softer walls to encourage boundary packing
+ initial_growth_pressure = 1.015 # Higher initial pressure for stronger exploration
+ final_growth_pressure = 1.0005 # Lower final pressure for smoother transition to fine-tuning
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 250 # More iterations for precise settling
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d8b70cd02cf01ce1f4cb7c08d8829ad74d35c7a2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/original.py
@@ -0,0 +1,199 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..2bf969452b45de9e9e2b2674b8b9aba6321533e6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results
+Run 1/1 completed in 21.46 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.933497329605997
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1005, 0.8995)\n centers[5] = (0.3039, 0.0961)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7001, 0.4958)\n centers[18] = (0.6950, 0.6982)\n centers[19] = (0.7001, 0.9054)\n centers[20] = (0.8999, 0.0983)\n centers[21] = (0.8990, 0.2989)\n centers[22] = (0.8990, 0.5022)\n centers[23] = (0.9041, 0.7010)\n centers[24] = (0.8993, 0.8996)\n centers[25] = (0.1961, 0.2039)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.933497329605997}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/packing_viz.png
+ execution_time_mean: 21.458947375416756
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..60fc1cb5c6239db87330a9b732d4516c2015f38f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.933497329605997,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1005, 0.8995)\n centers[5] = (0.3039, 0.0961)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7001, 0.4958)\n centers[18] = (0.6950, 0.6982)\n centers[19] = (0.7001, 0.9054)\n centers[20] = (0.8999, 0.0983)\n centers[21] = (0.8990, 0.2989)\n centers[22] = (0.8990, 0.5022)\n centers[23] = (0.9041, 0.7010)\n centers[24] = (0.8993, 0.8996)\n centers[25] = (0.1961, 0.2039)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.933497329605997
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/results/packing_viz.png",
+ "execution_time_mean": 21.458947375416756,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fbfd0e8de7ef141c7c91442c544114f5235d3247
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_33/search_replace.txt
@@ -0,0 +1,47 @@
+
+enhanced_simulation_tuning
+
+
+
+This change refines the simulation parameters to enable a deeper and more effective search for optimal packing configurations. It consists of three synergistic adjustments:
+
+1. **Increased Simulation Duration**: The number of iterations for both the main simulation phase (from 750 to 1000) and the fine-tuning phase (from 100 to 250) are increased. This provides more time for the system to explore the configuration space and settle into a higher-quality local minimum.
+
+2. **Reduced Wall Repulsion**: The `wall_repulsion_strength` is decreased from 0.5 to 0.25. Softer walls allow circles to pack more tightly against the boundaries of the square. Since the largest circles in an optimal packing are often found along the edges and in the corners, this change directly facilitates the growth of these critical circles, which has a significant impact on the total sum of radii.
+
+3. **Refined Growth Pressure Schedule**: The `initial_growth_pressure` is increased slightly (1.01 to 1.015) for a more energetic start, helping to break out of the initial grid symmetry. The `final_growth_pressure` is decreased (1.001 to 1.0005) to be closer to 1.0, ensuring a smoother transition into the non-pressurized fine-tuning phase.
+
+These parameter adjustments are designed to work together to guide the simulation toward denser, more optimal packings that better utilize the entire square, especially the boundaries.
+
+
+
+<<<<<<< SEARCH
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+=======
+ # --- Hyperparameters for the simulation ---
+ # Longer simulation with adjusted forces to find better minima.
+ iterations = 1000
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.25 # Softer walls to encourage boundary packing
+ initial_growth_pressure = 1.015 # Higher initial pressure for stronger exploration
+ final_growth_pressure = 1.0005 # Lower final pressure for smoother transition to fine-tuning
+>>>>>>> REPLACE
+
+
+
+<<<<<<< SEARCH
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+=======
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 250 # More iterations for precise settling
+ fine_tune_learning_rate = 0.001
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..9ccec2b12c86497b35b237a9a18e6b06461f605a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/edit.diff
@@ -0,0 +1,293 @@
+--- a/original.py
++++ b/original.py
+@@ -1,200 +1,236 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ Improved by combining insights from high-scoring prior programs:
+ - Optimized initial placement of the 26th circle.
+ - Reduced initial perturbation noise.
+ - Tuned learning rate for fine-grained adjustments.
+ - Increased iterations for radius calculation convergence.
+ - Robust handling of very close circle centers in radius calculation.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+- growth_pressure = 1.005 # Reintroduced from 1.92 program for denser packing
++ initial_growth_pressure = 1.01
++ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+- # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+- # --- Simulation Loop ---
++ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+- # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+- # 2. Calculate forces to push centers to a better configuration
+- forces = np.zeros((n, 2))
+-
+- # Create "inflated" radii to generate repulsive forces even when just touching
+- inflated_radii = radii * growth_pressure
+-
++ # Anneal growth pressure
++ current_growth_pressure = initial_growth_pressure - \
++ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
++ inflated_radii = radii * current_growth_pressure
++
++ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers_for_sim[i] - centers_for_sim[j]
+- dist = np.linalg.norm(vec)
+-
+- if dist < 1e-9:
+- continue
+-
+- # Use inflated radii for overlap calculation
+- overlap = inflated_radii[i] + inflated_radii[j] - dist
+-
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec
+-
+- # b. Wall repulsion forces, also using inflated radii
+- for i in range(n):
+- # Left wall
+- overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
+-
+- # 3. Update center positions based on forces
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
++
++ overlaps = np.maximum(0, sum_inflated_radii - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
++
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ # b. Vectorized Wall repulsion forces
++ wall_forces = np.zeros_like(centers_for_sim)
++ overlap_left = inflated_radii - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
++ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
++
++ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
++ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
++ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
++ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
++
++ forces = circle_forces + wall_forces
++
++ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+-
+- # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+- # Return the best configuration found across all trials
+- final_centers = best_final_centers
+- final_radii = best_final_radii
++ # --- Post-Simulation Fine-Tuning Phase ---
++ # Run a few more iterations with a very small learning rate and no growth pressure
++ # to allow centers to settle precisely, without aggressive expansion.
++ fine_tune_iterations = 100 # Increased due to efficiency
++ fine_tune_learning_rate = 0.001
++ centers_for_sim_fine_tune = best_final_centers.copy() # Start fine-tuning from the best configuration
++
++ for _ in range(fine_tune_iterations):
++ radii = compute_max_radii(centers_for_sim_fine_tune)
++
++ # --- Vectorized Force Calculation (no growth pressure) ---
++ # a. Circle-to-circle forces
++ diffs = centers_for_sim_fine_tune[:, np.newaxis, :] - centers_for_sim_fine_tune[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
++
++ overlaps = np.maximum(0, sum_radii_pairs - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
++
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ # b. Wall forces
++ wall_forces = np.zeros_like(centers_for_sim_fine_tune)
++ overlap_left = radii - centers_for_sim_fine_tune[:, 0]
++ overlap_right = (centers_for_sim_fine_tune[:, 0] + radii) - 1.0
++ if overlap_right > 0: # Check for overlap before adding force
++ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
++ overlap_bottom = radii - centers_for_sim_fine_tune[:, 1]
++ if overlap_bottom > 0: # Check for overlap before adding force
++ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
++ overlap_top = (centers_for_sim_fine_tune[:, 1] + radii) - 1.0
++ if overlap_top > 0: # Check for overlap before adding force
++ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
++ # Left wall
++ overlap_left = radii - centers_for_sim_fine_tune[:, 0]
++ if overlap_left > 0: # Check for overlap before adding force
++ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
++
++ forces = circle_forces + wall_forces
++
++ centers_for_sim_fine_tune += forces * fine_tune_learning_rate
++ centers_for_sim_fine_tune = np.clip(centers_for_sim_fine_tune, 0.0, 1.0)
++
++ final_centers = centers_for_sim_fine_tune
++ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence (from 100 to 200)
+- for _ in range(200):
++ # Increased iterations for better convergence (from 200 to 500, aligning with higher scores)
++ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..78f181a856ad5f5444c8d341404df1cc15f0c838
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/main.py
@@ -0,0 +1,236 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Run a few more iterations with a very small learning rate and no growth pressure
+ # to allow centers to settle precisely, without aggressive expansion.
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim_fine_tune = best_final_centers.copy() # Start fine-tuning from the best configuration
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim_fine_tune)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim_fine_tune[:, np.newaxis, :] - centers_for_sim_fine_tune[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim_fine_tune)
+ overlap_left = radii - centers_for_sim_fine_tune[:, 0]
+ overlap_right = (centers_for_sim_fine_tune[:, 0] + radii) - 1.0
+ if overlap_right > 0: # Check for overlap before adding force
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ overlap_bottom = radii - centers_for_sim_fine_tune[:, 1]
+ if overlap_bottom > 0: # Check for overlap before adding force
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ overlap_top = (centers_for_sim_fine_tune[:, 1] + radii) - 1.0
+ if overlap_top > 0: # Check for overlap before adding force
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+ # Left wall
+ overlap_left = radii - centers_for_sim_fine_tune[:, 0]
+ if overlap_left > 0: # Check for overlap before adding force
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim_fine_tune += forces * fine_tune_learning_rate
+ centers_for_sim_fine_tune = np.clip(centers_for_sim_fine_tune, 0.0, 1.0)
+
+ final_centers = centers_for_sim_fine_tune
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 200 to 500, aligning with higher scores)
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e78a43f372d7dd124e17e2c41d226d246b584f8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/original.py
@@ -0,0 +1,200 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 500 # Increased iterations for better convergence (from 1.92 program)
+ learning_rate = 0.01 # Tuned learning rate for fine-grained adjustments (from 1.92 program)
+ wall_repulsion_strength = 0.5
+ growth_pressure = 1.005 # Reintroduced from 1.92 program for denser packing
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Use inflated radii for overlap calculation
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy() # Store a copy
+ best_final_radii = current_final_radii.copy() # Store a copy
+
+ # Return the best configuration found across all trials
+ final_centers = best_final_centers
+ final_radii = best_final_radii
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..bd69456f59a71fa7398cb883a6cbaaacf3960ad5
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": false,
+ "error": "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3ad33fea518625783ad1a15ccad8e47390aca34b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_34/search_replace.txt
@@ -0,0 +1,270 @@
+
+Reintroducing vectorized force calculation and annealed growth pressure.
+The current program's description implies it has vectorized force calculations and tuned (annealed) growth pressure, but its implementation uses inefficient nested loops for force calculation and a constant growth pressure. This edit reintroduces the more efficient vectorized approach and the annealing schedule for growth pressure, which were present in the higher-scoring 1.93 program. This is a critical fix to align the code with its intended high-performance strategy.
+
+
+
+<<<<<<< SEARCH
+ wall_repulsion_strength = 0.5
+ growth_pressure = 1.005 # Reintroduced from 1.92 program for denser packing
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ # The current best result uses [0.05, 0.05], let's explore other symmetric options.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Simulation Loop ---
+ for sim_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers_for_sim)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Use inflated radii for overlap calculation
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap_left = inflated_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + inflated_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = inflated_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + inflated_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+=======
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+ best_final_radii = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], # Original effective position
+ [0.05, 0.95], # Top-left corner
+ [0.95, 0.05], # Bottom-right corner
+ [0.95, 0.95], # Top-right corner
+ [0.2, 0.2], # Position that yielded 1.92 in a prior program
+ [0.2, 0.8], # Symmetric to [0.2, 0.2]
+ [0.8, 0.2], # Symmetric to [0.2, 0.2]
+ [0.8, 0.8], # Symmetric to [0.2, 0.2]
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ # Start with a 5x5 grid, as this has shown to be a strong base configuration.
+ # Re-initialize centers for each trial to ensure independent simulations.
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle at the current candidate position.
+ current_centers[25] = initial_pos_26th_circle
+
+ # Use a copy of centers for the simulation, as it modifies the array
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+>>>>>>> REPLACE
+
+
+
+Reintroduce the fine-tuning phase after the main simulation.
+The current program inexplicably removed the dedicated fine-tuning phase that was present in the higher-scoring 1.93 program. This phase is crucial for precise adjustments without aggressive growth pressure, allowing the circles to settle into a more optimal arrangement. This edit restores this important two-phase simulation strategy.
+
+
+
+<<<<<<< SEARCH
+ # Return the best configuration found across all trials
+ final_centers = best_final_centers
+ final_radii = best_final_radii
+
+ return final_centers, final_radii
+=======
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Run a few more iterations with a very small learning rate and no growth pressure
+ # to allow centers to settle precisely, without aggressive expansion.
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim_fine_tune = best_final_centers.copy() # Start fine-tuning from the best configuration
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim_fine_tune)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim_fine_tune[:, np.newaxis, :] - centers_for_sim_fine_tune[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim_fine_tune)
+ overlap_left = radii - centers_for_sim_fine_tune[:, 0]
+ overlap_right = (centers_for_sim_fine_tune[:, 0] + radii) - 1.0
+ if overlap_right > 0: # Check for overlap before adding force
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ overlap_bottom = radii - centers_for_sim_fine_tune[:, 1]
+ if overlap_bottom > 0: # Check for overlap before adding force
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ overlap_top = (centers_for_sim_fine_tune[:, 1] + radii) - 1.0
+ if overlap_top > 0: # Check for overlap before adding force
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+ # Left wall
+ overlap_left = radii - centers_for_sim_fine_tune[:, 0]
+ if overlap_left > 0: # Check for overlap before adding force
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim_fine_tune += forces * fine_tune_learning_rate
+ centers_for_sim_fine_tune = np.clip(centers_for_sim_fine_tune, 0.0, 1.0)
+
+ final_centers = centers_for_sim_fine_tune
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+>>>>>>> REPLACE
+
+
+
+Increase compute_max_radii iterations for better convergence.
+The description for the current program explicitly states "Increased iterations for radius calculation convergence", yet its `compute_max_radii` function uses 200 iterations while the higher-scoring 1.93 program used 500. This is a regression that limits the precision of radius calculations. Increasing the iterations to 500 will ensure better convergence and more accurate radii determination.
+
+
+
+<<<<<<< SEARCH
+ # Increased iterations for better convergence (from 100 to 200)
+ for _ in range(200):
+=======
+ # Increased iterations for better convergence (from 200 to 500, aligning with higher scores)
+ for _ in range(500):
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..55dc37609c44dcd397c19001fc1c5f1efc3ca1b9
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..a850ebb04487e4e3f4e4aeb89158ba7d67156a07
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/edit.diff
@@ -0,0 +1,430 @@
+--- a/original.py
++++ b/original.py
+@@ -1,199 +1,257 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+-This version is a crossover, combining the high-level strategy of the best-performing
+-parent (multi-start, two-phase simulation, annealed growth pressure) with the
+-efficient vectorized force calculations from the inspiration program.
++This version refactors the simulation into a modular class-based structure
++to improve organization, maintainability, and allow for more advanced
++annealing schedules and initialization strategies.
+ """
+
+ import numpy as np
+
+-def construct_packing():
+- """
+- Constructs a packing of 26 circles by simulating physical repulsion forces
+- to find an optimal arrangement of centers.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- n = 26
+-
+- # --- Hyperparameters for the simulation ---
+- # Longer simulation with adjusted forces to find better minima.
+- iterations = 1000
+- learning_rate = 0.01
+- wall_repulsion_strength = 0.25 # Softer walls to encourage boundary packing
+- initial_growth_pressure = 1.015 # Higher initial pressure for stronger exploration
+- final_growth_pressure = 1.0005 # Lower final pressure for smoother transition to fine-tuning
+-
+- best_sum_radii = -1.0
+- best_final_centers = None
+-
+- # Candidate initial positions for the 26th circle to optimize corner utilization.
+- candidate_extra_circle_initial_positions = [
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+- [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+- ]
+-
+- for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+- # --- Initialization for current trial ---
+- current_centers = np.zeros((n, 2))
++# Helper function (remains outside the class as it's stateless)
++def compute_max_radii(centers):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ This uses an iterative proportional scaling method.
++ """
++ n = centers.shape[0]
++ radii = np.ones(n)
++
++ # First, limit by distance to square borders
++ for i in range(n):
++ x, y = centers[i]
++ radii[i] = min(x, y, 1 - x, 1 - y)
++
++ # Then, limit by distance to other circles using iterative proportional scaling
++ # Increased iterations for stability based on prior insights
++ for _ in range(500):
++ updated_in_pass = False
++ for i in range(n):
++ for j in range(i + 1, n):
++ dist = np.linalg.norm(centers[i] - centers[j])
++
++ if dist < 1e-9: # Robust handling for very close centers
++ radii[i] = 0.0
++ radii[j] = 0.0
++ updated_in_pass = True
++ continue
++
++ if radii[i] + radii[j] > dist:
++ updated_in_pass = True
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++
++ if not updated_in_pass:
++ break
++
++ return np.maximum(radii, 0.0)
++
++class CirclePackingOptimizer:
++ """
++ Encapsulates the logic for optimizing circle packing using a physical simulation approach.
++ Manages hyperparameters, initialization, force calculations, and simulation phases.
++ """
++ def __init__(self, n=26,
++ iterations_main=1000, learning_rate_main=0.01,
++ initial_growth_pressure=1.015, final_growth_pressure=1.0005,
++ wall_repulsion_strength=0.25,
++ fine_tune_iterations=250, fine_tune_learning_rate=0.001,
++ fine_tune_growth_pressure_start=1.0, fine_tune_growth_pressure_end=1.0,
++ initial_perturbation_scale=0.0):
++
++ self.n = n
++ self.iterations_main = iterations_main
++ self.learning_rate_main = learning_rate_main
++ self.initial_growth_pressure = initial_growth_pressure
++ self.final_growth_pressure = final_growth_pressure
++ self.wall_repulsion_strength = wall_repulsion_strength
++ self.fine_tune_iterations = fine_tune_iterations
++ self.fine_tune_learning_rate = fine_tune_learning_rate
++ self.fine_tune_growth_pressure_start = fine_tune_growth_pressure_start
++ self.fine_tune_growth_pressure_end = fine_tune_growth_pressure_end
++ self.initial_perturbation_scale = initial_perturbation_scale # Recommendation 4
++
++ self.best_sum_radii = -1.0
++ self.best_final_centers = None
++
++ def _initialize_centers(self, extra_pos_26th_circle):
++ """
++ Initializes centers with a 5x5 grid and places the 26th circle.
++ Applies a small random perturbation if `self.initial_perturbation_scale` is > 0.
++ (Implementation for Recommendation 4)
++ """
++ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+- current_centers[k, 0] = (i + 0.5) * spacing
+- current_centers[k, 1] = (j + 0.5) * spacing
++ centers[k, 0] = (i + 0.5) * spacing
++ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+- current_centers[25] = initial_pos_26th_circle
+-
+- centers_for_sim = current_centers.copy()
+-
+- # --- Main Simulation Loop (Vectorized) ---
++ centers[25] = extra_pos_26th_circle
++
++ if self.initial_perturbation_scale > 0:
++ centers += np.random.normal(0, spacing * self.initial_perturbation_scale, size=centers.shape)
++ centers = np.clip(centers, 0.0, 1.0) # Ensure centers stay within bounds after perturbation
++
++ return centers
++
++ def _calculate_forces(self, centers, radii, growth_pressure_factor):
++ """
++ Calculates total forces (circle-to-circle and wall repulsion) for a given state.
++ Uses vectorized operations for efficiency.
++ """
++ forces = np.zeros_like(centers)
++
++ # Apply growth pressure to radii
++ inflated_radii = radii * growth_pressure_factor
++
++ # a. Circle-to-circle repulsion forces
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
++
++ # Calculate overlaps, ensuring no self-overlap
++ overlaps = np.maximum(0, sum_inflated_radii - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ # Calculate unit vectors for directions
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0 # Avoid division by zero for identical centers
++
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++ forces += circle_forces
++
++ # b. Wall repulsion forces
++ overlap_left = inflated_radii - centers[:, 0]
++ overlap_right = (centers[:, 0] + inflated_radii) - 1.0
++ overlap_bottom = inflated_radii - centers[:, 1]
++ overlap_top = (centers[:, 1] + inflated_radii) - 1.0
++
++ forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, overlap_left)
++ forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, overlap_right)
++ forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, overlap_bottom)
++ forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, overlap_top)
++
++ return forces
++
++ def _run_simulation_phase(self, initial_centers, iterations, learning_rate_init,
++ growth_pressure_anneal_start=1.0, growth_pressure_anneal_end=1.0,
++ learning_rate_annealing_exponent=2.0):
++ """
++ Runs a single simulation phase with specified parameters, applying annealing schedules.
++ (Implements Recommendation 2 for growth pressure annealing)
++ """
++ centers_for_sim = initial_centers.copy()
++
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+- # Anneal growth pressure
+- current_growth_pressure = initial_growth_pressure - \
+- (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+- inflated_radii = radii * current_growth_pressure
+-
+- # --- Vectorized Force Calculation ---
+- # a. Circle-to-circle repulsion forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_inflated_radii - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- # b. Vectorized Wall repulsion forces
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = inflated_radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+- overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces = circle_forces + wall_forces
+-
+- # --- Update center positions ---
+- current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
++ # Anneal growth pressure (quadratic schedule, Recommendation 2)
++ growth_pressure_factor = growth_pressure_anneal_end + \
++ (growth_pressure_anneal_start - growth_pressure_anneal_end) * \
++ (1.0 - (sim_iter / iterations))**2
++
++ forces = self._calculate_forces(centers_for_sim, radii, growth_pressure_factor)
++
++ # Anneal learning rate (quadratic schedule for main, linear for fine-tune)
++ current_lr = learning_rate_init * (1.0 - (sim_iter / iterations))**learning_rate_annealing_exponent
++
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # --- Finalization for current trial ---
+- current_final_radii = compute_max_radii(centers_for_sim)
+- current_sum_radii = np.sum(current_final_radii)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_final_centers = centers_for_sim.copy()
+-
+- # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 250 # More iterations for precise settling
+- fine_tune_learning_rate = 0.001
+- centers_for_sim = best_final_centers.copy()
+-
+- for _ in range(fine_tune_iterations):
+- radii = compute_max_radii(centers_for_sim)
+-
+- # --- Vectorized Force Calculation (no growth pressure) ---
+- # a. Circle-to-circle forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_radii_pairs - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- # b. Wall forces
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+- overlap_bottom = radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces = circle_forces + wall_forces
+-
+- centers_for_sim += forces * fine_tune_learning_rate
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- final_centers = centers_for_sim
+- final_radii = compute_max_radii(final_centers)
+-
+- return final_centers, final_radii
+-
+-
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This uses an iterative proportional scaling method.
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- for _ in range(500):
+- updated_in_pass = False
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9:
+- radii[i] = 0.0
+- radii[j] = 0.0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0.0)
++
++ return centers_for_sim
++
++ def solve(self):
++ """
++ Orchestrates the multi-start simulation and fine-tuning phases.
++ """
++ # Candidate initial positions for the 26th circle (Recommendation 1 implicitly handled by multi-start)
++ candidate_extra_circle_initial_positions = [
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
++ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
++ ]
++
++ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
++ # --- Main Simulation Phase ---
++ # Initialize centers for the current trial, with optional small perturbation
++ initial_centers_trial = self._initialize_centers(initial_pos_26th_circle)
++
++ simulated_centers = self._run_simulation_phase(
++ initial_centers_trial,
++ self.iterations_main,
++ self.learning_rate_main,
++ self.initial_growth_pressure,
++ self.final_growth_pressure,
++ learning_rate_annealing_exponent=2.0 # Quadratic LR annealing for main phase
++ )
++
++ current_sum_radii = np.sum(compute_max_radii(simulated_centers))
++
++ if current_sum_radii > self.best_sum_radii:
++ self.best_sum_radii = current_sum_radii
++ self.best_final_centers = simulated_centers.copy()
++
++ # --- Post-Simulation Fine-Tuning Phase ---
++ # Start fine-tuning from the best configuration found in the main phase.
++ if self.best_final_centers is not None:
++ fine_tuned_centers = self._run_simulation_phase(
++ self.best_final_centers,
++ self.fine_tune_iterations,
++ self.fine_tune_learning_rate,
++ self.fine_tune_growth_pressure_start, # Start with a subtle growth pressure (Recommendation 3)
++ self.fine_tune_growth_pressure_end, # Anneal it down for precise packing (Recommendation 3)
++ learning_rate_annealing_exponent=1.0 # Linear LR annealing for fine-tuning
++ )
++ final_centers = fine_tuned_centers
++ else: # Fallback if no valid centers were found (should not happen with good candidates)
++ final_centers = self._initialize_centers([0.5, 0.5]) # Default to center if nothing better found
++
++ final_radii = compute_max_radii(final_centers)
++
++ return final_centers, final_radii
++
++def construct_packing():
++ """
++ Constructs a packing of 26 circles by simulating physical repulsion forces
++ to find an optimal arrangement of centers.
++
++ Returns:
++ Tuple of (centers, radii)
++ centers: np.array of shape (26, 2) with (x, y) coordinates
++ radii: np.array of shape (26) with radius of each circle
++ """
++ # Instantiate the optimizer with desired hyperparameters.
++ # These parameters are tuned based on observations and recommendations.
++ optimizer = CirclePackingOptimizer(
++ n=26,
++ iterations_main=1200, # Increased for more thorough exploration
++ learning_rate_main=0.01,
++ initial_growth_pressure=1.018, # Slightly higher initial pressure for stronger repulsion
++ final_growth_pressure=1.0001, # Closer to 1.0 for precise packing at end of main phase
++ wall_repulsion_strength=0.3, # Slightly adjusted wall repulsion
++ fine_tune_iterations=300, # Increased fine-tune iterations for precision
++ fine_tune_learning_rate=0.0008, # Slower fine-tuning for high precision
++ fine_tune_growth_pressure_start=1.0005, # Small growth pressure in fine-tune (Recommendation 3)
++ fine_tune_growth_pressure_end=1.00001, # Almost zero growth pressure at the end of fine-tune (Recommendation 3)
++ initial_perturbation_scale=0.005 # Small random perturbation to initial grid (Recommendation 4)
++ )
++
++ return optimizer.solve()
++
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd7727832559fed2bfc20f38feb32e88df4889e7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/main.py
@@ -0,0 +1,257 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version refactors the simulation into a modular class-based structure
+to improve organization, maintainability, and allow for more advanced
+annealing schedules and initialization strategies.
+"""
+
+import numpy as np
+
+# Helper function (remains outside the class as it's stateless)
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for stability based on prior insights
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Robust handling for very close centers
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+class CirclePackingOptimizer:
+ """
+ Encapsulates the logic for optimizing circle packing using a physical simulation approach.
+ Manages hyperparameters, initialization, force calculations, and simulation phases.
+ """
+ def __init__(self, n=26,
+ iterations_main=1000, learning_rate_main=0.01,
+ initial_growth_pressure=1.015, final_growth_pressure=1.0005,
+ wall_repulsion_strength=0.25,
+ fine_tune_iterations=250, fine_tune_learning_rate=0.001,
+ fine_tune_growth_pressure_start=1.0, fine_tune_growth_pressure_end=1.0,
+ initial_perturbation_scale=0.0):
+
+ self.n = n
+ self.iterations_main = iterations_main
+ self.learning_rate_main = learning_rate_main
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.wall_repulsion_strength = wall_repulsion_strength
+ self.fine_tune_iterations = fine_tune_iterations
+ self.fine_tune_learning_rate = fine_tune_learning_rate
+ self.fine_tune_growth_pressure_start = fine_tune_growth_pressure_start
+ self.fine_tune_growth_pressure_end = fine_tune_growth_pressure_end
+ self.initial_perturbation_scale = initial_perturbation_scale # Recommendation 4
+
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_pos_26th_circle):
+ """
+ Initializes centers with a 5x5 grid and places the 26th circle.
+ Applies a small random perturbation if `self.initial_perturbation_scale` is > 0.
+ (Implementation for Recommendation 4)
+ """
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_pos_26th_circle
+
+ if self.initial_perturbation_scale > 0:
+ centers += np.random.normal(0, spacing * self.initial_perturbation_scale, size=centers.shape)
+ centers = np.clip(centers, 0.0, 1.0) # Ensure centers stay within bounds after perturbation
+
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """
+ Calculates total forces (circle-to-circle and wall repulsion) for a given state.
+ Uses vectorized operations for efficiency.
+ """
+ forces = np.zeros_like(centers)
+
+ # Apply growth pressure to radii
+ inflated_radii = radii * growth_pressure_factor
+
+ # a. Circle-to-circle repulsion forces
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ # Calculate overlaps, ensuring no self-overlap
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ # Calculate unit vectors for directions
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0 # Avoid division by zero for identical centers
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+ forces += circle_forces
+
+ # b. Wall repulsion forces
+ overlap_left = inflated_radii - centers[:, 0]
+ overlap_right = (centers[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers[:, 1]
+ overlap_top = (centers[:, 1] + inflated_radii) - 1.0
+
+ forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, overlap_left)
+ forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, overlap_right)
+ forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ return forces
+
+ def _run_simulation_phase(self, initial_centers, iterations, learning_rate_init,
+ growth_pressure_anneal_start=1.0, growth_pressure_anneal_end=1.0,
+ learning_rate_annealing_exponent=2.0):
+ """
+ Runs a single simulation phase with specified parameters, applying annealing schedules.
+ (Implements Recommendation 2 for growth pressure annealing)
+ """
+ centers_for_sim = initial_centers.copy()
+
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure (quadratic schedule, Recommendation 2)
+ growth_pressure_factor = growth_pressure_anneal_end + \
+ (growth_pressure_anneal_start - growth_pressure_anneal_end) * \
+ (1.0 - (sim_iter / iterations))**2
+
+ forces = self._calculate_forces(centers_for_sim, radii, growth_pressure_factor)
+
+ # Anneal learning rate (quadratic schedule for main, linear for fine-tune)
+ current_lr = learning_rate_init * (1.0 - (sim_iter / iterations))**learning_rate_annealing_exponent
+
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ return centers_for_sim
+
+ def solve(self):
+ """
+ Orchestrates the multi-start simulation and fine-tuning phases.
+ """
+ # Candidate initial positions for the 26th circle (Recommendation 1 implicitly handled by multi-start)
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Main Simulation Phase ---
+ # Initialize centers for the current trial, with optional small perturbation
+ initial_centers_trial = self._initialize_centers(initial_pos_26th_circle)
+
+ simulated_centers = self._run_simulation_phase(
+ initial_centers_trial,
+ self.iterations_main,
+ self.learning_rate_main,
+ self.initial_growth_pressure,
+ self.final_growth_pressure,
+ learning_rate_annealing_exponent=2.0 # Quadratic LR annealing for main phase
+ )
+
+ current_sum_radii = np.sum(compute_max_radii(simulated_centers))
+
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = simulated_centers.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Start fine-tuning from the best configuration found in the main phase.
+ if self.best_final_centers is not None:
+ fine_tuned_centers = self._run_simulation_phase(
+ self.best_final_centers,
+ self.fine_tune_iterations,
+ self.fine_tune_learning_rate,
+ self.fine_tune_growth_pressure_start, # Start with a subtle growth pressure (Recommendation 3)
+ self.fine_tune_growth_pressure_end, # Anneal it down for precise packing (Recommendation 3)
+ learning_rate_annealing_exponent=1.0 # Linear LR annealing for fine-tuning
+ )
+ final_centers = fine_tuned_centers
+ else: # Fallback if no valid centers were found (should not happen with good candidates)
+ final_centers = self._initialize_centers([0.5, 0.5]) # Default to center if nothing better found
+
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Instantiate the optimizer with desired hyperparameters.
+ # These parameters are tuned based on observations and recommendations.
+ optimizer = CirclePackingOptimizer(
+ n=26,
+ iterations_main=1200, # Increased for more thorough exploration
+ learning_rate_main=0.01,
+ initial_growth_pressure=1.018, # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure=1.0001, # Closer to 1.0 for precise packing at end of main phase
+ wall_repulsion_strength=0.3, # Slightly adjusted wall repulsion
+ fine_tune_iterations=300, # Increased fine-tune iterations for precision
+ fine_tune_learning_rate=0.0008, # Slower fine-tuning for high precision
+ fine_tune_growth_pressure_start=1.0005, # Small growth pressure in fine-tune (Recommendation 3)
+ fine_tune_growth_pressure_end=1.00001, # Almost zero growth pressure at the end of fine-tune (Recommendation 3)
+ initial_perturbation_scale=0.005 # Small random perturbation to initial grid (Recommendation 4)
+ )
+
+ return optimizer.solve()
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c9de1a96e46a8a927c680f45ed56ac1decd7429
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/original.py
@@ -0,0 +1,199 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Longer simulation with adjusted forces to find better minima.
+ iterations = 1000
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.25 # Softer walls to encourage boundary packing
+ initial_growth_pressure = 1.015 # Higher initial pressure for stronger exploration
+ final_growth_pressure = 1.0005 # Lower final pressure for smoother transition to fine-tuning
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 250 # More iterations for precise settling
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..74ea75606e02a3e440a2bd6b4a645f0ab2b613a6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results
+Run 1/1 completed in 24.84 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9336859917072435
+ public: {'centers_str': ' centers[0] = (0.1007, 0.0993)\n centers[1] = (0.1001, 0.2991)\n centers[2] = (0.0992, 0.5012)\n centers[3] = (0.1001, 0.7014)\n centers[4] = (0.1007, 0.9004)\n centers[5] = (0.3051, 0.0948)\n centers[6] = (0.3009, 0.2992)\n centers[7] = (0.3013, 0.5005)\n centers[8] = (0.3008, 0.7009)\n centers[9] = (0.3001, 0.8988)\n centers[10] = (0.4982, 0.0994)\n centers[11] = (0.4996, 0.3001)\n centers[12] = (0.4981, 0.5009)\n centers[13] = (0.4994, 0.6996)\n centers[14] = (0.4994, 0.9000)\n centers[15] = (0.6978, 0.0992)\n centers[16] = (0.7013, 0.3003)\n centers[17] = (0.7003, 0.4960)\n centers[18] = (0.6955, 0.6978)\n centers[19] = (0.7010, 0.9051)\n centers[20] = (0.8999, 0.0985)\n centers[21] = (0.8990, 0.2989)\n centers[22] = (0.8984, 0.5027)\n centers[23] = (0.9042, 0.7019)\n centers[24] = (0.9003, 0.9000)\n centers[25] = (0.1980, 0.2047)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9336859917072435}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/packing_viz.png
+ execution_time_mean: 24.837295938748866
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..522455f22a4d7e106911c949d3c0cb8030d12737
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9336859917072435,
+ "public": {
+ "centers_str": " centers[0] = (0.1007, 0.0993)\n centers[1] = (0.1001, 0.2991)\n centers[2] = (0.0992, 0.5012)\n centers[3] = (0.1001, 0.7014)\n centers[4] = (0.1007, 0.9004)\n centers[5] = (0.3051, 0.0948)\n centers[6] = (0.3009, 0.2992)\n centers[7] = (0.3013, 0.5005)\n centers[8] = (0.3008, 0.7009)\n centers[9] = (0.3001, 0.8988)\n centers[10] = (0.4982, 0.0994)\n centers[11] = (0.4996, 0.3001)\n centers[12] = (0.4981, 0.5009)\n centers[13] = (0.4994, 0.6996)\n centers[14] = (0.4994, 0.9000)\n centers[15] = (0.6978, 0.0992)\n centers[16] = (0.7013, 0.3003)\n centers[17] = (0.7003, 0.4960)\n centers[18] = (0.6955, 0.6978)\n centers[19] = (0.7010, 0.9051)\n centers[20] = (0.8999, 0.0985)\n centers[21] = (0.8990, 0.2989)\n centers[22] = (0.8984, 0.5027)\n centers[23] = (0.9042, 0.7019)\n centers[24] = (0.9003, 0.9000)\n centers[25] = (0.1980, 0.2047)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9336859917072435
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/results/packing_viz.png",
+ "execution_time_mean": 24.837295938748866,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f503f8de29317484be8d11b2e6856c2f9e9f4012
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_35/rewrite.txt
@@ -0,0 +1,248 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version refactors the simulation into a modular class-based structure
+to improve organization, maintainability, and allow for more advanced
+annealing schedules and initialization strategies.
+"""
+
+import numpy as np
+
+# Helper function (remains outside the class as it's stateless)
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for stability based on prior insights
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Robust handling for very close centers
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+class CirclePackingOptimizer:
+ """
+ Encapsulates the logic for optimizing circle packing using a physical simulation approach.
+ Manages hyperparameters, initialization, force calculations, and simulation phases.
+ """
+ def __init__(self, n=26,
+ iterations_main=1000, learning_rate_main=0.01,
+ initial_growth_pressure=1.015, final_growth_pressure=1.0005,
+ wall_repulsion_strength=0.25,
+ fine_tune_iterations=250, fine_tune_learning_rate=0.001,
+ fine_tune_growth_pressure_start=1.0, fine_tune_growth_pressure_end=1.0,
+ initial_perturbation_scale=0.0):
+
+ self.n = n
+ self.iterations_main = iterations_main
+ self.learning_rate_main = learning_rate_main
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.wall_repulsion_strength = wall_repulsion_strength
+ self.fine_tune_iterations = fine_tune_iterations
+ self.fine_tune_learning_rate = fine_tune_learning_rate
+ self.fine_tune_growth_pressure_start = fine_tune_growth_pressure_start
+ self.fine_tune_growth_pressure_end = fine_tune_growth_pressure_end
+ self.initial_perturbation_scale = initial_perturbation_scale # Recommendation 4
+
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_pos_26th_circle):
+ """
+ Initializes centers with a 5x5 grid and places the 26th circle.
+ Applies a small random perturbation if `self.initial_perturbation_scale` is > 0.
+ (Implementation for Recommendation 4)
+ """
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_pos_26th_circle
+
+ if self.initial_perturbation_scale > 0:
+ centers += np.random.normal(0, spacing * self.initial_perturbation_scale, size=centers.shape)
+ centers = np.clip(centers, 0.0, 1.0) # Ensure centers stay within bounds after perturbation
+
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """
+ Calculates total forces (circle-to-circle and wall repulsion) for a given state.
+ Uses vectorized operations for efficiency.
+ """
+ forces = np.zeros_like(centers)
+
+ # Apply growth pressure to radii
+ inflated_radii = radii * growth_pressure_factor
+
+ # a. Circle-to-circle repulsion forces
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ # Calculate overlaps, ensuring no self-overlap
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ # Calculate unit vectors for directions
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0 # Avoid division by zero for identical centers
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+ forces += circle_forces
+
+ # b. Wall repulsion forces
+ overlap_left = inflated_radii - centers[:, 0]
+ overlap_right = (centers[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers[:, 1]
+ overlap_top = (centers[:, 1] + inflated_radii) - 1.0
+
+ forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, overlap_left)
+ forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, overlap_right)
+ forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ return forces
+
+ def _run_simulation_phase(self, initial_centers, iterations, learning_rate_init,
+ growth_pressure_anneal_start=1.0, growth_pressure_anneal_end=1.0,
+ learning_rate_annealing_exponent=2.0):
+ """
+ Runs a single simulation phase with specified parameters, applying annealing schedules.
+ (Implements Recommendation 2 for growth pressure annealing)
+ """
+ centers_for_sim = initial_centers.copy()
+
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure (quadratic schedule, Recommendation 2)
+ growth_pressure_factor = growth_pressure_anneal_end + \
+ (growth_pressure_anneal_start - growth_pressure_anneal_end) * \
+ (1.0 - (sim_iter / iterations))**2
+
+ forces = self._calculate_forces(centers_for_sim, radii, growth_pressure_factor)
+
+ # Anneal learning rate (quadratic schedule for main, linear for fine-tune)
+ current_lr = learning_rate_init * (1.0 - (sim_iter / iterations))**learning_rate_annealing_exponent
+
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ return centers_for_sim
+
+ def solve(self):
+ """
+ Orchestrates the multi-start simulation and fine-tuning phases.
+ """
+ # Candidate initial positions for the 26th circle (Recommendation 1 implicitly handled by multi-start)
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Main Simulation Phase ---
+ # Initialize centers for the current trial, with optional small perturbation
+ initial_centers_trial = self._initialize_centers(initial_pos_26th_circle)
+
+ simulated_centers = self._run_simulation_phase(
+ initial_centers_trial,
+ self.iterations_main,
+ self.learning_rate_main,
+ self.initial_growth_pressure,
+ self.final_growth_pressure,
+ learning_rate_annealing_exponent=2.0 # Quadratic LR annealing for main phase
+ )
+
+ current_sum_radii = np.sum(compute_max_radii(simulated_centers))
+
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = simulated_centers.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Start fine-tuning from the best configuration found in the main phase.
+ if self.best_final_centers is not None:
+ fine_tuned_centers = self._run_simulation_phase(
+ self.best_final_centers,
+ self.fine_tune_iterations,
+ self.fine_tune_learning_rate,
+ self.fine_tune_growth_pressure_start, # Start with a subtle growth pressure (Recommendation 3)
+ self.fine_tune_growth_pressure_end, # Anneal it down for precise packing (Recommendation 3)
+ learning_rate_annealing_exponent=1.0 # Linear LR annealing for fine-tuning
+ )
+ final_centers = fine_tuned_centers
+ else: # Fallback if no valid centers were found (should not happen with good candidates)
+ final_centers = self._initialize_centers([0.5, 0.5]) # Default to center if nothing better found
+
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Instantiate the optimizer with desired hyperparameters.
+ # These parameters are tuned based on observations and recommendations.
+ optimizer = CirclePackingOptimizer(
+ n=26,
+ iterations_main=1200, # Increased for more thorough exploration
+ learning_rate_main=0.01,
+ initial_growth_pressure=1.018, # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure=1.0001, # Closer to 1.0 for precise packing at end of main phase
+ wall_repulsion_strength=0.3, # Slightly adjusted wall repulsion
+ fine_tune_iterations=300, # Increased fine-tune iterations for precision
+ fine_tune_learning_rate=0.0008, # Slower fine-tuning for high precision
+ fine_tune_growth_pressure_start=1.0005, # Small growth pressure in fine-tune (Recommendation 3)
+ fine_tune_growth_pressure_end=1.00001, # Almost zero growth pressure at the end of fine-tune (Recommendation 3)
+ initial_perturbation_scale=0.005 # Small random perturbation to initial grid (Recommendation 4)
+ )
+
+ return optimizer.solve()
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..35019b5a257e828f85047ab92a63696cda989f67
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..a6c8bf82210b555390c17ae5be39607678a02898
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/edit.diff
@@ -0,0 +1,262 @@
+--- a/original.py
++++ b/original.py
+@@ -1,173 +1,217 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ Improved by combining insights from high-scoring prior programs:
+ - Optimized initial placement of the 26th circle.
+ - Reduced initial perturbation noise.
+ - Tuned learning rate for fine-grained adjustments.
+ - Increased iterations for radius calculation convergence.
+ - Robust handling of very close circle centers in radius calculation.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+- # --- Hyperparameters for the simulation (tuned from high-scoring program) ---
+- iterations = 500 # Increased iterations for better convergence
+- learning_rate = 0.02 # A balanced learning rate for exploration.
+- wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+- growth_pressure = 1.005 # Key parameter: gentle expansion pressure to fill gaps.
++ # --- Hyperparameters for the simulation (from high-scoring class-based packer) ---
++ iterations = 500 # Iterations for main refinement simulation.
++ learning_rate = 0.02 # Base learning rate for refinement.
++ wall_repulsion_strength = 0.8 # Repulsion strength from boundaries.
++ initial_growth_pressure = 1.02 # Start with high pressure to break symmetry.
++ final_growth_pressure = 1.001 # End with gentle pressure to tighten packing.
++ fine_tune_iter = 100 # Iterations for final settlement.
++ fine_tune_lr = 0.001 # Small, constant LR for fine-tuning.
+
+ # --- Initialization ---
+ # Start with a 5x5 grid plus one interstitial circle. This is a proven high-potential starting configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the interstitial void at (0.2, 0.2)
+ centers[25] = [spacing, spacing]
+
+- # --- Simulation Loop ---
+- # The simulation refines center positions using a force-directed layout algorithm.
+- # It uses a "growth pressure" concept to actively seek more efficient packings.
++ # --- Refinement Simulation Loop (with Annealed Growth Pressure) ---
++ # This phase uses a high, decaying growth pressure to "melt" the initial
++ # grid and find a globally better arrangement.
+ for sim_iter in range(iterations):
+- # 1. Calculate the maximum non-overlapping radii for the current positions.
++ # Anneal growth pressure quadratically.
++ progress = sim_iter / iterations
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++
++ # 1. Calculate radii for current positions.
+ radii = compute_max_radii(centers)
+
+- # 2. Artificially inflate radii to create an expansion 'pressure'.
+- # This is the key to escaping local minima and finding denser packings.
+- pressured_radii = radii * growth_pressure
+-
+- # 3. Calculate forces to push centers to a better configuration.
++ # 2. Artificially inflate radii to create expansion 'pressure'.
++ pressured_radii = radii * current_growth_pressure
++
++ # 3. Calculate forces based on artificial overlaps.
+ forces = np.zeros((n, 2))
+
+- # a. Circle-to-circle repulsion based on artificial overlap
++ # a) Circle-to-circle repulsion
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+-
+- if dist < 1e-9:
+- continue
+-
+- # Calculate overlap using the inflated, 'pressured' radii
++ if dist < 1e-9: continue
++
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+-
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+- # b. Wall repulsion forces, also using pressured radii
+- for i in range(n):
+- # Left wall
+- overlap_left = pressured_radii[i] - centers[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers[i, 0] + pressured_radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = pressured_radii[i] - centers[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers[i, 1] + pressured_radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
+-
+- # 4. Update center positions with an annealing learning rate for stability.
++ # b) Wall repulsion
++ for i in range(n):
++ overlap_l = pressured_radii[i] - centers[i, 0]
++ if overlap_l > 0: forces[i, 0] += wall_repulsion_strength * overlap_l
++
++ overlap_r = (centers[i, 0] + pressured_radii[i]) - 1.0
++ if overlap_r > 0: forces[i, 0] -= wall_repulsion_strength * overlap_r
++
++ overlap_b = pressured_radii[i] - centers[i, 1]
++ if overlap_b > 0: forces[i, 1] += wall_repulsion_strength * overlap_b
++
++ overlap_t = (centers[i, 1] + pressured_radii[i]) - 1.0
++ if overlap_t > 0: forces[i, 1] -= wall_repulsion_strength * overlap_t
++
++ # 4. Update positions with a decaying learning rate.
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 5. Enforce hard boundary constraints.
+ centers = np.clip(centers, 0.0, 1.0)
+
++ # --- Fine-Tuning Loop ---
++ # This second phase has no growth pressure. It allows the packing to settle
++ # and resolve minor overlaps, leading to a more precise final state.
++ for _ in range(fine_tune_iter):
++ # 1. Calculate radii for the current positions. NO growth pressure.
++ radii = compute_max_radii(centers)
++
++ forces = np.zeros((n, 2))
++
++ # 2. Calculate repulsion forces based on *actual* overlaps.
++ # a) Circle-to-circle repulsion
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers[i] - centers[j]
++ dist = np.linalg.norm(vec)
++ if dist < 1e-9: continue
++
++ # Overlap is based on actual radii.
++ overlap = radii[i] + radii[j] - dist
++ if overlap > 0:
++ force_magnitude = overlap
++ force_vec = (vec / dist) * force_magnitude
++ forces[i] += force_vec
++ forces[j] -= force_vec
++
++ # b) Wall repulsion (based on actual radii)
++ for i in range(n):
++ overlap_l = radii[i] - centers[i, 0]
++ if overlap_l > 0: forces[i, 0] += wall_repulsion_strength * overlap_l
++
++ overlap_r = (centers[i, 0] + radii[i]) - 1.0
++ if overlap_r > 0: forces[i, 0] -= wall_repulsion_strength * overlap_r
++
++ overlap_b = radii[i] - centers[i, 1]
++ if overlap_b > 0: forces[i, 1] += wall_repulsion_strength * overlap_b
++
++ overlap_t = (centers[i, 1] + radii[i]) - 1.0
++ if overlap_t > 0: forces[i, 1] -= wall_repulsion_strength * overlap_t
++
++ # 3. Update positions with a small, constant learning rate.
++ centers += forces * fine_tune_lr
++
++ # 4. Enforce hard boundary constraints.
++ centers = np.clip(centers, 0.0, 1.0)
++
++
+ # --- Finalization ---
+- # After the simulation, compute the final, precise radii for the optimized centers.
++ # After all simulations, compute the final, precise radii for the optimized centers.
+ final_radii = compute_max_radii(centers)
+ final_centers = centers
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence, matching high-scoring programs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..7b1e542918bea4e98ce4020e12c0274c45d70152
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/main.py
@@ -0,0 +1,217 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation (from high-scoring class-based packer) ---
+ iterations = 500 # Iterations for main refinement simulation.
+ learning_rate = 0.02 # Base learning rate for refinement.
+ wall_repulsion_strength = 0.8 # Repulsion strength from boundaries.
+ initial_growth_pressure = 1.02 # Start with high pressure to break symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten packing.
+ fine_tune_iter = 100 # Iterations for final settlement.
+ fine_tune_lr = 0.001 # Small, constant LR for fine-tuning.
+
+ # --- Initialization ---
+ # Start with a 5x5 grid plus one interstitial circle. This is a proven high-potential starting configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the interstitial void at (0.2, 0.2)
+ centers[25] = [spacing, spacing]
+
+ # --- Refinement Simulation Loop (with Annealed Growth Pressure) ---
+ # This phase uses a high, decaying growth pressure to "melt" the initial
+ # grid and find a globally better arrangement.
+ for sim_iter in range(iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for current positions.
+ radii = compute_max_radii(centers)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'.
+ pressured_radii = radii * current_growth_pressure
+
+ # 3. Calculate forces based on artificial overlaps.
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion
+ for i in range(n):
+ overlap_l = pressured_radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_repulsion_strength * overlap_l
+
+ overlap_r = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_repulsion_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_repulsion_strength * overlap_b
+
+ overlap_t = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_repulsion_strength * overlap_t
+
+ # 4. Update positions with a decaying learning rate.
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 5. Enforce hard boundary constraints.
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii = compute_max_radii(centers)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps.
+ # a) Circle-to-circle repulsion
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Overlap is based on actual radii.
+ overlap = radii[i] + radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(n):
+ overlap_l = radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_repulsion_strength * overlap_l
+
+ overlap_r = (centers[i, 0] + radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_repulsion_strength * overlap_r
+
+ overlap_b = radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_repulsion_strength * overlap_b
+
+ overlap_t = (centers[i, 1] + radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_repulsion_strength * overlap_t
+
+ # 3. Update positions with a small, constant learning rate.
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers = np.clip(centers, 0.0, 1.0)
+
+
+ # --- Finalization ---
+ # After all simulations, compute the final, precise radii for the optimized centers.
+ final_radii = compute_max_radii(centers)
+ final_centers = centers
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence, matching high-scoring programs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..46e6498ef4412e636b351042d79badbab6737584
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/original.py
@@ -0,0 +1,173 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation (tuned from high-scoring program) ---
+ iterations = 500 # Increased iterations for better convergence
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ growth_pressure = 1.005 # Key parameter: gentle expansion pressure to fill gaps.
+
+ # --- Initialization ---
+ # Start with a 5x5 grid plus one interstitial circle. This is a proven high-potential starting configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the interstitial void at (0.2, 0.2)
+ centers[25] = [spacing, spacing]
+
+ # --- Simulation Loop ---
+ # The simulation refines center positions using a force-directed layout algorithm.
+ # It uses a "growth pressure" concept to actively seek more efficient packings.
+ for sim_iter in range(iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ radii = compute_max_radii(centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the key to escaping local minima and finding denser packings.
+ pressured_radii = radii * growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration.
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion based on artificial overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Calculate overlap using the inflated, 'pressured' radii
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using pressured radii
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 4. Update center positions with an annealing learning rate for stability.
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 5. Enforce hard boundary constraints.
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, compute the final, precise radii for the optimized centers.
+ final_radii = compute_max_radii(centers)
+ final_centers = centers
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence, matching high-scoring programs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..6f1c79b77d95f16a61a79ef32904cf1f3c6cbaa3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results
+Run 1/1 completed in 1.97 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.937208711337219
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1009, 0.8991)\n centers[5] = (0.3043, 0.0957)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7001, 0.4949)\n centers[18] = (0.6938, 0.6980)\n centers[19] = (0.6999, 0.9053)\n centers[20] = (0.8995, 0.0973)\n centers[21] = (0.8971, 0.2976)\n centers[22] = (0.8971, 0.5030)\n centers[23] = (0.9035, 0.7024)\n centers[24] = (0.8988, 0.8997)\n centers[25] = (0.1957, 0.2043)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.937208711337219}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/packing_viz.png
+ execution_time_mean: 1.9740931540727615
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..dcad4f088092bef576431ba6f204803dc446a7e7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.937208711337219,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1009, 0.8991)\n centers[5] = (0.3043, 0.0957)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7001, 0.4949)\n centers[18] = (0.6938, 0.6980)\n centers[19] = (0.6999, 0.9053)\n centers[20] = (0.8995, 0.0973)\n centers[21] = (0.8971, 0.2976)\n centers[22] = (0.8971, 0.5030)\n centers[23] = (0.9035, 0.7024)\n centers[24] = (0.8988, 0.8997)\n centers[25] = (0.1957, 0.2043)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.937208711337219
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/results/packing_viz.png",
+ "execution_time_mean": 1.9740931540727615,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0c818621e7ca51f799f353d2b409562ea228195a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_36/search_replace.txt
@@ -0,0 +1,223 @@
+Excellent, I will proceed with a two-part improvement inspired by the most successful prior implementation. The core idea is to replace the current simple, single-phase simulation with a more sophisticated two-phase process:
+
+1. **Refinement Phase:** This phase uses a high, but quadratically annealing, "growth pressure". This starts with a strong outward force to "melt" the initial rigid grid configuration, allowing the circles to rearrange into a more globally optimal structure. The pressure then gradually decreases, allowing the system to cool and settle.
+2. **Fine-Tuning Phase:** Following the refinement, this phase runs with no growth pressure and a very small learning rate. Its sole purpose is to resolve any minor remaining overlaps from the aggressive first phase and let the circles gently nudge each other into their final, precise, non-overlapping positions.
+
+This "annealing" and "fine-tuning" approach is a classic and powerful strategy in computational geometry optimization. It effectively balances large-scale exploration with local, high-precision exploitation, which is key to escaping the local minimum of the initial grid and finding a denser packing.
+
+I will implement this in two sequential edits. First, I will update the hyperparameters to support the new two-phase logic. Second, I will replace the entire simulation block with the new implementation.
+
+
+introduce_advanced_hyperparams
+
+
+
+I am replacing the simple set of simulation hyperparameters with a more comprehensive set that supports a two-phase simulation strategy. I am removing the single `growth_pressure` and introducing separate `initial_growth_pressure` and `final_growth_pressure` for an annealing schedule. Additionally, I am adding `fine_tune_iter` and `fine_tune_lr` to control the new fine-tuning phase. These values are directly adopted from the high-scoring prior program that used a class-based `ConfigurableHybridPacker`.
+
+
+
+<<<<<<< SEARCH
+ # --- Hyperparameters for the simulation (tuned from high-scoring program) ---
+ iterations = 500 # Increased iterations for better convergence
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ growth_pressure = 1.005 # Key parameter: gentle expansion pressure to fill gaps.
+=======
+ # --- Hyperparameters for the simulation (from high-scoring class-based packer) ---
+ iterations = 500 # Iterations for main refinement simulation.
+ learning_rate = 0.02 # Base learning rate for refinement.
+ wall_repulsion_strength = 0.8 # Repulsion strength from boundaries.
+ initial_growth_pressure = 1.02 # Start with high pressure to break symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten packing.
+ fine_tune_iter = 100 # Iterations for final settlement.
+ fine_tune_lr = 0.001 # Small, constant LR for fine-tuning.
+>>>>>>> REPLACE
+
+
+implement_two_phase_simulation
+
+
+
+I am replacing the entire single-phase simulation logic with a more advanced two-phase process.
+
+1. **Refinement Loop:** This first loop implements the annealed growth pressure, starting high at `initial_growth_pressure` and decaying quadratically to `final_growth_pressure`. This strong initial force is designed to break the initial grid symmetry and explore a much wider range of configurations.
+
+2. **Fine-Tuning Loop:** This new, second loop runs after the refinement phase. It operates with **no** growth pressure and a very small, constant learning rate (`fine_tune_lr`). Its purpose is to gently resolve any minor overlaps created during the aggressive refinement phase, allowing the configuration to settle into a precise, stable, and locally optimal packing.
+
+This combined strategy is directly inspired by the most successful prior program and is a standard technique for complex optimization problems, ensuring both global exploration and local precision.
+
+
+
+<<<<<<< SEARCH
+ # --- Simulation Loop ---
+ # The simulation refines center positions using a force-directed layout algorithm.
+ # It uses a "growth pressure" concept to actively seek more efficient packings.
+ for sim_iter in range(iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ radii = compute_max_radii(centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the key to escaping local minima and finding denser packings.
+ pressured_radii = radii * growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration.
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion based on artificial overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Calculate overlap using the inflated, 'pressured' radii
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using pressured radii
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 4. Update center positions with an annealing learning rate for stability.
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 5. Enforce hard boundary constraints.
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, compute the final, precise radii for the optimized centers.
+ final_radii = compute_max_radii(centers)
+ final_centers = centers
+
+ return final_centers, final_radii
+=======
+ # --- Refinement Simulation Loop (with Annealed Growth Pressure) ---
+ # This phase uses a high, decaying growth pressure to "melt" the initial
+ # grid and find a globally better arrangement.
+ for sim_iter in range(iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for current positions.
+ radii = compute_max_radii(centers)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'.
+ pressured_radii = radii * current_growth_pressure
+
+ # 3. Calculate forces based on artificial overlaps.
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion
+ for i in range(n):
+ overlap_l = pressured_radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_repulsion_strength * overlap_l
+
+ overlap_r = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_repulsion_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_repulsion_strength * overlap_b
+
+ overlap_t = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_repulsion_strength * overlap_t
+
+ # 4. Update positions with a decaying learning rate.
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 5. Enforce hard boundary constraints.
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii = compute_max_radii(centers)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps.
+ # a) Circle-to-circle repulsion
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Overlap is based on actual radii.
+ overlap = radii[i] + radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(n):
+ overlap_l = radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_repulsion_strength * overlap_l
+
+ overlap_r = (centers[i, 0] + radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_repulsion_strength * overlap_r
+
+ overlap_b = radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_repulsion_strength * overlap_b
+
+ overlap_t = (centers[i, 1] + radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_repulsion_strength * overlap_t
+
+ # 3. Update positions with a small, constant learning rate.
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers = np.clip(centers, 0.0, 1.0)
+
+
+ # --- Finalization ---
+ # After all simulations, compute the final, precise radii for the optimized centers.
+ final_radii = compute_max_radii(centers)
+ final_centers = centers
+
+ return final_centers, final_radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b3692266e7c7a1524891e9f418f84fb2ce973135
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..648a70002436014dcc63a4789bf9452f5067f7db
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/edit.diff
@@ -0,0 +1,255 @@
+--- a/original.py
++++ b/original.py
+@@ -1,216 +1,223 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+- # a) Circle-to-circle repulsion
+- for i in range(self.n):
+- for j in range(i + 1, self.n):
+- vec = self.centers[i] - self.centers[j]
+- dist = np.linalg.norm(vec)
+- if dist < 1e-9: continue
+-
+- overlap = pressured_radii[i] + pressured_radii[j] - dist
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec
++ # a) Circle-to-circle repulsion (vectorized)
++ # Calculate pairwise differences and distances
++ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++
++ # Prevent division by zero for co-located circles
++ dists[dists < 1e-9] = 1e-9
++
++ # Calculate pairwise radius sums and then overlaps
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = radii_sums - dists
++ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
++ np.fill_diagonal(overlaps, 0) # No self-repulsion
++
++ # Calculate force magnitudes and apply them along the normalized difference vectors
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++
++ # Sum forces for each circle
++ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion
+ for i in range(self.n):
+ overlap_l = pressured_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+- # a) Circle-to-circle repulsion
+- for i in range(self.n):
+- for j in range(i + 1, self.n):
+- vec = self.centers[i] - self.centers[j]
+- dist = np.linalg.norm(vec)
+- if dist < 1e-9: continue
+-
+- # Overlap is based on actual radii.
+- overlap = current_radii[i] + current_radii[j] - dist
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec
++ # a) Circle-to-circle repulsion (vectorized)
++ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++
++ # Overlap is based on actual radii.
++ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
++ overlaps = radii_sums - dists
++ overlaps[overlaps < 0] = 0.0
++ np.fill_diagonal(overlaps, 0)
++
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(self.n):
+ overlap_l = current_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = current_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+ def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary.
+ # We introduce an annealing schedule for growth pressure to break out of
+ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+- 'sim_iter': 500, # More iterations for thorough refinement.
++ 'sim_iter': 1000, # Increased iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+- 'fine_tune_iter': 100, # Iterations for final settlement without growth pressure.
++ 'fine_tune_iter': 200, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..366062572268f1157bc37130701427d81ac9bdd1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/main.py
@@ -0,0 +1,223 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion
+ for i in range(self.n):
+ overlap_l = pressured_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(self.n):
+ overlap_l = current_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = current_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary.
+ # We introduce an annealing schedule for growth pressure to break out of
+ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+ 'sim_iter': 1000, # Increased iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 200, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..60b8484c14953d1706ea5db2b33fbefa3d6e837d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/original.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ vec = self.centers[i] - self.centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion
+ for i in range(self.n):
+ overlap_l = pressured_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ vec = self.centers[i] - self.centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Overlap is based on actual radii.
+ overlap = current_radii[i] + current_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(self.n):
+ overlap_l = current_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = current_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary.
+ # We introduce an annealing schedule for growth pressure to break out of
+ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+ 'sim_iter': 500, # More iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 100, # Iterations for final settlement without growth pressure.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..901d0702b5f9465c4350a94b1ad6cca8043bffa4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results
+Run 1/1 completed in 3.02 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9454836647028526
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3029, 0.0971)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8990, 0.1010)\n centers[5] = (0.0918, 0.3082)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.4861, 0.6984)\n centers[18] = (0.6994, 0.6893)\n centers[19] = (0.9094, 0.7000)\n centers[20] = (0.0945, 0.8996)\n centers[21] = (0.2951, 0.8939)\n centers[22] = (0.5054, 0.8959)\n centers[23] = (0.7049, 0.9046)\n centers[24] = (0.9003, 0.8985)\n centers[25] = (0.2053, 0.1947)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9454836647028526}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/packing_viz.png
+ execution_time_mean: 3.022138264030218
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..a756f6fae6127a55b788e157c9836a4917addf8c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9454836647028526,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3029, 0.0971)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8990, 0.1010)\n centers[5] = (0.0918, 0.3082)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.4861, 0.6984)\n centers[18] = (0.6994, 0.6893)\n centers[19] = (0.9094, 0.7000)\n centers[20] = (0.0945, 0.8996)\n centers[21] = (0.2951, 0.8939)\n centers[22] = (0.5054, 0.8959)\n centers[23] = (0.7049, 0.9046)\n centers[24] = (0.9003, 0.8985)\n centers[25] = (0.2053, 0.1947)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9454836647028526
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/results/packing_viz.png",
+ "execution_time_mean": 3.022138264030218,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1610d56ab2d9017da889456cac3a077d8fc2ea90
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_37/search_replace.txt
@@ -0,0 +1,108 @@
+
+vectorize_and_extend_simulation
+
+
+
+This edit introduces a significant performance optimization by vectorizing the circle-to-circle repulsion force calculations. The original implementation used nested Python loops, which are computationally expensive. By replacing these with vectorized NumPy operations, we can leverage highly optimized, low-level code to perform the same calculations much faster.
+
+This optimization is applied to both the main `_run_refinement_simulation` and the `_run_fine_tuning` methods. The core idea is to compute all pairwise distances and overlaps in matrix form, calculate a matrix of force vectors, and then sum the forces for each circle.
+
+The substantial speedup gained from vectorization provides a larger computational budget. I will leverage this by increasing the number of simulation iterations. Specifically, `sim_iter` is increased from 500 to 1000 and `fine_tune_iter` from 100 to 200. This allows the force-directed algorithm to run for longer, enabling a more thorough exploration of the configuration space and leading to a more refined and densely packed final arrangement.
+
+
+
+<<<<<<< SEARCH
+ # a) Circle-to-circle repulsion
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ vec = self.centers[i] - self.centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+=======
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ # a) Circle-to-circle repulsion
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ vec = self.centers[i] - self.centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Overlap is based on actual radii.
+ overlap = current_radii[i] + current_radii[j] - dist
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+=======
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ packer_config = {
+ 'sim_iter': 500, # More iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 100, # Iterations for final settlement without growth pressure.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+=======
+ packer_config = {
+ 'sim_iter': 1000, # Increased iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 200, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e9c5f5f770e330f81526036e23d15f8731bb1e6a
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..99bba8611d54cc77ec73fb50cc2a2957f24f4ca7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/edit.diff
@@ -0,0 +1,451 @@
+--- a/original.py
++++ b/original.py
+@@ -1,256 +1,211 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
+-
+ import numpy as np
+
+-
+-def construct_packing():
+- """
+- Construct a specific arrangement of 26 circles in a unit square
+- that attempts to maximize the sum of their radii.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- # Initialize arrays for 26 circles
+- n = 26
+-
+- # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
++def compute_max_radii(centers, radius_iter=250):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ This uses an iterative proportional scaling method.
++ """
++ n = centers.shape[0]
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ # Iteratively adjust radii to resolve overlaps and maximize size
++ for _ in range(radius_iter):
++ updated = False
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers[i] - centers[j]
++ dist = np.linalg.norm(vec)
++
++ # Handle co-located centers: set radii to zero to avoid division by zero and severe overlap
++ if dist < 1e-9:
++ if radii[i] > 0 or radii[j] > 0: # Only update if they had positive radii
++ radii[i], radii[j] = 0.0, 0.0
++ updated = True
++ continue
++
++ # If current radii would cause overlap, scale them down proportionally
++ if radii[i] + radii[j] > dist:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++ if not updated:
++ break # Radii have stabilized
++ return np.maximum(radii, 0.0) # Ensure no negative radii
++
++class Individual:
++ """Represents a single candidate packing solution (a set of circle centers)."""
++ def __init__(self, centers_array, n_circles):
++ self.centers = np.array(centers_array).reshape(n_circles, 2)
++ self.radii = None
++ self.fitness = -1.0 # Sum of radii, initialized to a low value
++
++ def evaluate_fitness(self, radius_iter_val):
++ """Calculates the sum of radii for the current center configuration."""
++ self.radii = compute_max_radii(self.centers, radius_iter=radius_iter_val)
++ self.fitness = np.sum(self.radii)
++ return self.fitness
++
++def initialize_population(n_circles, population_size):
++ """
++ Initializes a diverse population of individuals for the Genetic Algorithm.
++ Includes a known good starting grid and random configurations.
++ """
++ population = []
++
++ # 1. Add the best-known 5x5 grid + 1 interstitial configuration as a strong baseline
+ num_cells_side = 5
+- spacing = 1.0 / num_cells_side # spacing is 0.2
+-
+- base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
++ spacing = 1.0 / num_cells_side
++ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+- base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+- base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
++ grid_centers[k, 0] = (i + 0.5) * spacing
++ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+-
+- # Define strategic candidate placements for the 26th circle.
+- # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+- candidate_extra_positions = [
+- # Interstitial points from 5x5 grid
+- [spacing, spacing], # (0.2, 0.2)
+- [spacing, 0.5], # (0.2, 0.5)
+- [spacing, 1 - spacing], # (0.2, 0.8)
+- [0.5, spacing], # (0.5, 0.2)
+- [0.5, 0.5], # (0.5, 0.5) - Center
+- [0.5, 1 - spacing], # (0.5, 0.8)
+- [1 - spacing, spacing], # (0.8, 0.2)
+- [1 - spacing, 0.5], # (0.8, 0.5)
+- [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- # Corner-offset points
+- [0.05, 0.05],
+- [0.05, 0.95],
+- [0.95, 0.05],
+- [0.95, 0.95]
+- ]
+-
+- best_sum_radii = 0
+- best_centers = None
+- best_radii = None
+-
+- # --- Simulation Hyperparameters ---
+- # Tuned based on high-scoring prior programs.
+- simulation_iterations = 500 # Number of steps for force-directed simulation
+- initial_learning_rate = 0.02 # A more stable learning rate for refinement.
+- wall_repulsion_strength = 0.8 # Stronger walls to utilize boundary space.
+- growth_pressure = 1.005 # Key parameter to create expansion force.
+-
+- # Iterate through candidate positions for the 26th circle to find the best one.
+- for extra_pos in candidate_extra_positions:
+- current_centers = np.zeros((n, 2))
+- current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+- current_centers[25] = np.array(extra_pos)
+-
+- # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+- centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+-
+- for sim_iter in range(simulation_iterations):
+- # 1. Calculate radii for the current positions.
+- radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+-
+- # 1.5. Artificially inflate radii to create an expansion 'pressure'.
+- # This is the crucial step that drives the simulation.
+- pressured_radii = radii_for_sim * growth_pressure
+-
+- # 2. Calculate forces to push centers to a better configuration
+- forces = np.zeros((n, 2))
+-
+- # a. Circle-to-circle repulsion forces (based on pressured radii)
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers_for_sim[i] - centers_for_sim[j]
+- dist = np.linalg.norm(vec)
+-
+- if dist < 1e-9: # Centers are too close.
+- continue
+-
+- # Calculate overlap based on the artificially inflated radii.
+- overlap = pressured_radii[i] + pressured_radii[j] - dist
+-
+- if overlap > 0:
+- # Force magnitude proportional to overlap
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec # Equal and opposite reaction
+-
+- # b. Wall repulsion forces (based on pressured radii)
+- for i in range(n):
+- # Left wall
+- overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
+-
+- # 3. Update center positions based on forces
+- # Annealing learning rate: starts high, decreases over time
+- current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+- centers_for_sim += forces * current_lr
+-
+- # 4. Enforce boundary constraints - keep centers within [0,1]
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # After simulation, use the refined centers to compute FINAL, accurate radii
+- current_radii = compute_max_radii(centers_for_sim)
+- current_sum_radii = np.sum(current_radii)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers = centers_for_sim.copy() # Store the optimized centers
+- best_radii = current_radii.copy()
+-
+- # Fallback if no valid configuration was found (should not happen with good candidates)
+- if best_centers is None:
+- # This fallback uses the base 5x5 + center as a safe default
+- final_centers = np.zeros((n, 2))
+- final_centers[:25] = base_centers
+- final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+- final_radii = compute_max_radii(final_centers)
+- return final_centers, final_radii
+- else:
+- return best_centers, best_radii
+-
+-
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This function performs a full, highly convergent iteration.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence for final radii
+- for _ in range(500):
+- updated_in_pass = False
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- # If current radii would cause overlap and distance is not zero
+- if dist < 1e-9: # Handle cases where centers might be identical or very close
+- # If centers are practically the same, they cannot both have positive radius.
+- # For packing, it's safer to consider them as overlapping severely and shrink them.
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- # Scale both radii proportionally to just touch
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- # If a full pass over all pairs results in no change, radii have stabilized
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0) # Ensure no negative radii
+-
+-
+-def compute_radii_for_simulation(centers):
+- """
+- Compute approximate maximum radii for each circle position
+- for use inside the simulation loop. Fewer iterations for performance.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+- for _ in range(250):
+- updated_in_pass = False
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9:
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0)
+-
++ grid_centers[n_circles - 1] = [spacing, spacing] # The 26th circle at (0.2, 0.2)
++ population.append(Individual(grid_centers, n_circles))
++
++ # 2. Add some slightly perturbed versions of this grid configuration
++ for _ in range(population_size // 10): # Create a few perturbed grid individuals
++ # Small Gaussian noise to break perfect symmetry
++ perturbed_grid_centers = grid_centers + np.random.normal(0, 0.01, (n_circles, 2))
++ perturbed_grid_centers = np.clip(perturbed_grid_centers, 0.0, 1.0) # Keep within bounds
++ population.append(Individual(perturbed_grid_centers, n_circles))
++
++ # 3. Fill the rest of the population with random initializations for broad diversity
++ while len(population) < population_size:
++ random_centers = np.random.rand(n_circles, 2)
++ population.append(Individual(random_centers, n_circles))
++
++ return population
++
++def select_parents(population, tournament_size):
++ """
++ Selects two parents using tournament selection.
++ Randomly picks `tournament_size` individuals and selects the fittest among them.
++ """
++ # Select parent 1
++ tournament_candidates = np.random.choice(population, tournament_size, replace=False)
++ parent1 = max(tournament_candidates, key=lambda ind: ind.fitness)
++
++ # Select parent 2, ensuring it's different from parent 1 if possible
++ tournament_candidates_for_p2 = [ind for ind in tournament_candidates if ind != parent1]
++ if len(tournament_candidates_for_p2) > 0:
++ parent2 = max(tournament_candidates_for_p2, key=lambda ind: ind.fitness)
++ else: # Fallback: if only one distinct candidate (e.g., tournament_size=1, or all candidates were parent1)
++ parent2 = np.random.choice(population)
++ while parent2 == parent1: # Ensure different parents if possible
++ parent2 = np.random.choice(population)
++
++ return parent1, parent2
++
++def crossover(parent1, parent2, n_circles):
++ """
++ Performs uniform crossover: for each circle, its position (x,y) is inherited
++ from either parent1 or parent2 with 50% probability.
++ """
++ child_centers = np.zeros((n_circles, 2))
++ for i in range(n_circles):
++ if np.random.rand() < 0.5: # 50% chance to inherit this circle's position from parent1
++ child_centers[i] = parent1.centers[i]
++ else: # Otherwise, inherit from parent2
++ child_centers[i] = parent2.centers[i]
++
++ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to ensure within bounds
++
++def mutate(individual, mutation_rate, mutation_strength, n_circles):
++ """
++ Mutates an individual's circle center positions.
++ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
++ perturbed by Gaussian noise with `mutation_strength` standard deviation.
++ """
++ mutated_centers = individual.centers.copy()
++ for i in range(n_circles):
++ if np.random.rand() < mutation_rate:
++ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
++ mutated_centers[i] = np.clip(mutated_centers[i], 0.0, 1.0) # Keep within bounds
++ return Individual(mutated_centers, n_circles)
++
++def construct_packing():
++ """
++ Constructs a packing of 26 circles in a unit square using a Genetic Algorithm
++ to maximize the sum of their radii.
++ """
++ n_circles = 26
++
++ # --- Genetic Algorithm Hyperparameters ---
++ POPULATION_SIZE = 150 # Number of candidate solutions in each generation
++ NUM_GENERATIONS = 2500 # Total number of evolutionary steps
++ MUTATION_RATE = 0.15 # Probability that a single circle's position is mutated
++ INITIAL_MUTATION_STRENGTH = 0.08 # Initial standard deviation of Gaussian noise for mutation
++ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
++ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
++ ELITISM_COUNT = 10 # Number of best individuals to directly carry over to next generation
++
++ # Radius calculation iterations: lower for speed during GA, higher for final precision
++ GA_RADIUS_ITER = 60 # Iterations for `compute_max_radii` during GA evolution
++ FINAL_RADIUS_ITER = 350 # Iterations for final best solution evaluation
++
++ # Initialize the first generation of candidate packings
++ population = initialize_population(n_circles, POPULATION_SIZE)
++
++ # Evaluate fitness of the initial population
++ for individual in population:
++ individual.evaluate_fitness(GA_RADIUS_ITER)
++
++ # Keep track of the best individual found across all generations
++ best_individual_overall = max(population, key=lambda ind: ind.fitness)
++
++ # --- Main Genetic Algorithm Loop ---
++ for generation in range(NUM_GENERATIONS):
++ # Anneal mutation strength: decreases over time for fine-tuning
++ progress = generation / NUM_GENERATIONS
++ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
++ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
++
++ new_population = []
++
++ # Elitism: Directly carry over the best individuals to the next generation
++ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
++ new_population.extend(sorted_population[:ELITISM_COUNT])
++
++ # Generate the rest of the new population through selection, crossover, and mutation
++ while len(new_population) < POPULATION_SIZE:
++ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
++ child = crossover(parent1, parent2, n_circles)
++ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
++ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child with reduced iterations for speed
++ new_population.append(child)
++
++ population = new_population # Replace old population with the new one
++
++ # Update the overall best individual if a better one is found
++ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
++ if current_best_in_gen.fitness > best_individual_overall.fitness:
++ best_individual_overall = current_best_in_gen
++
++ # Optional: Print progress
++ # if generation % 100 == 0:
++ # print(f"Generation {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
++
++ # Final evaluation of the best individual with high `radius_iter` for maximum precision
++ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
++
++ return best_individual_overall.centers, best_individual_overall.radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..3e6c9456975053e27e79fcf079de69bc4a8ec0a1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/main.py
@@ -0,0 +1,211 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, radius_iter=250):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively adjust radii to resolve overlaps and maximize size
+ for _ in range(radius_iter):
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ # Handle co-located centers: set radii to zero to avoid division by zero and severe overlap
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0: # Only update if they had positive radii
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ continue
+
+ # If current radii would cause overlap, scale them down proportionally
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break # Radii have stabilized
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, radius_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population of individuals for the Genetic Algorithm.
+ Includes a known good starting grid and random configurations.
+ """
+ population = []
+
+ # 1. Add the best-known 5x5 grid + 1 interstitial configuration as a strong baseline
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing] # The 26th circle at (0.2, 0.2)
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add some slightly perturbed versions of this grid configuration
+ for _ in range(population_size // 10): # Create a few perturbed grid individuals
+ # Small Gaussian noise to break perfect symmetry
+ perturbed_grid_centers = grid_centers + np.random.normal(0, 0.01, (n_circles, 2))
+ perturbed_grid_centers = np.clip(perturbed_grid_centers, 0.0, 1.0) # Keep within bounds
+ population.append(Individual(perturbed_grid_centers, n_circles))
+
+ # 3. Fill the rest of the population with random initializations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """
+ Selects two parents using tournament selection.
+ Randomly picks `tournament_size` individuals and selects the fittest among them.
+ """
+ # Select parent 1
+ tournament_candidates = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_candidates, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1 if possible
+ tournament_candidates_for_p2 = [ind for ind in tournament_candidates if ind != parent1]
+ if len(tournament_candidates_for_p2) > 0:
+ parent2 = max(tournament_candidates_for_p2, key=lambda ind: ind.fitness)
+ else: # Fallback: if only one distinct candidate (e.g., tournament_size=1, or all candidates were parent1)
+ parent2 = np.random.choice(population)
+ while parent2 == parent1: # Ensure different parents if possible
+ parent2 = np.random.choice(population)
+
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ for i in range(n_circles):
+ if np.random.rand() < 0.5: # 50% chance to inherit this circle's position from parent1
+ child_centers[i] = parent1.centers[i]
+ else: # Otherwise, inherit from parent2
+ child_centers[i] = parent2.centers[i]
+
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to ensure within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Mutates an individual's circle center positions.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ for i in range(n_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+ mutated_centers[i] = np.clip(mutated_centers[i], 0.0, 1.0) # Keep within bounds
+ return Individual(mutated_centers, n_circles)
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles in a unit square using a Genetic Algorithm
+ to maximize the sum of their radii.
+ """
+ n_circles = 26
+
+ # --- Genetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 150 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 2500 # Total number of evolutionary steps
+ MUTATION_RATE = 0.15 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.08 # Initial standard deviation of Gaussian noise for mutation
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 10 # Number of best individuals to directly carry over to next generation
+
+ # Radius calculation iterations: lower for speed during GA, higher for final precision
+ GA_RADIUS_ITER = 60 # Iterations for `compute_max_radii` during GA evolution
+ FINAL_RADIUS_ITER = 350 # Iterations for final best solution evaluation
+
+ # Initialize the first generation of candidate packings
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Evaluate fitness of the initial population
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ # Keep track of the best individual found across all generations
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child with reduced iterations for speed
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual if a better one is found
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Optional: Print progress
+ # if generation % 100 == 0:
+ # print(f"Generation {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final evaluation of the best individual with high `radius_iter` for maximum precision
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b901cf614ca947e5bc73a16b33ef8ab284cd09e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/original.py
@@ -0,0 +1,256 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+ initial_learning_rate = 0.02 # A more stable learning rate for refinement.
+ wall_repulsion_strength = 0.8 # Stronger walls to utilize boundary space.
+ growth_pressure = 1.005 # Key parameter to create expansion force.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 1.5. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the crucial step that drives the simulation.
+ pressured_radii = radii_for_sim * growth_pressure
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ # Calculate overlap based on the artificially inflated radii.
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..1f8fcf8044e08af2893c727730b039b99a6d7a4c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results
+Run 1/1 completed in 936.88 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.2733649967426377
+ public: {'centers_str': ' centers[0] = (0.1011, 0.1014)\n centers[1] = (0.2665, 0.0676)\n centers[2] = (0.4329, 0.1019)\n centers[3] = (0.6301, 0.0921)\n centers[4] = (0.8931, 0.1072)\n centers[5] = (0.0898, 0.2927)\n centers[6] = (0.6146, 0.6298)\n centers[7] = (0.5010, 0.3087)\n centers[8] = (0.7085, 0.2361)\n centers[9] = (0.9072, 0.3065)\n centers[10] = (0.0899, 0.4742)\n centers[11] = (0.3124, 0.4622)\n centers[12] = (0.5190, 0.5060)\n centers[13] = (0.7004, 0.4920)\n centers[14] = (0.9051, 0.4941)\n centers[15] = (0.1216, 0.6767)\n centers[16] = (0.3873, 0.6774)\n centers[17] = (0.5679, 0.6137)\n centers[18] = (0.6861, 0.7009)\n centers[19] = (0.8939, 0.6942)\n centers[20] = (0.1012, 0.8986)\n centers[21] = (0.2993, 0.9034)\n centers[22] = (0.4980, 0.8978)\n centers[23] = (0.7000, 0.9000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.2815, 0.2476)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.2733649967426377}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/packing_viz.png
+ execution_time_mean: 936.8818088597618
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..6f5329ef0a722691c955fd4456db70a5c4e3fd4d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.2733649967426377,
+ "public": {
+ "centers_str": " centers[0] = (0.1011, 0.1014)\n centers[1] = (0.2665, 0.0676)\n centers[2] = (0.4329, 0.1019)\n centers[3] = (0.6301, 0.0921)\n centers[4] = (0.8931, 0.1072)\n centers[5] = (0.0898, 0.2927)\n centers[6] = (0.6146, 0.6298)\n centers[7] = (0.5010, 0.3087)\n centers[8] = (0.7085, 0.2361)\n centers[9] = (0.9072, 0.3065)\n centers[10] = (0.0899, 0.4742)\n centers[11] = (0.3124, 0.4622)\n centers[12] = (0.5190, 0.5060)\n centers[13] = (0.7004, 0.4920)\n centers[14] = (0.9051, 0.4941)\n centers[15] = (0.1216, 0.6767)\n centers[16] = (0.3873, 0.6774)\n centers[17] = (0.5679, 0.6137)\n centers[18] = (0.6861, 0.7009)\n centers[19] = (0.8939, 0.6942)\n centers[20] = (0.1012, 0.8986)\n centers[21] = (0.2993, 0.9034)\n centers[22] = (0.4980, 0.8978)\n centers[23] = (0.7000, 0.9000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.2815, 0.2476)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.2733649967426377
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/results/packing_viz.png",
+ "execution_time_mean": 936.8818088597618,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4f2e535df30a4e8092d872f1e7b8fac6f8deef24
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_38/rewrite.txt
@@ -0,0 +1,202 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, radius_iter=250):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively adjust radii to resolve overlaps and maximize size
+ for _ in range(radius_iter):
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ # Handle co-located centers: set radii to zero to avoid division by zero and severe overlap
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0: # Only update if they had positive radii
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ continue
+
+ # If current radii would cause overlap, scale them down proportionally
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break # Radii have stabilized
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, radius_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population of individuals for the Genetic Algorithm.
+ Includes a known good starting grid and random configurations.
+ """
+ population = []
+
+ # 1. Add the best-known 5x5 grid + 1 interstitial configuration as a strong baseline
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing] # The 26th circle at (0.2, 0.2)
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add some slightly perturbed versions of this grid configuration
+ for _ in range(population_size // 10): # Create a few perturbed grid individuals
+ # Small Gaussian noise to break perfect symmetry
+ perturbed_grid_centers = grid_centers + np.random.normal(0, 0.01, (n_circles, 2))
+ perturbed_grid_centers = np.clip(perturbed_grid_centers, 0.0, 1.0) # Keep within bounds
+ population.append(Individual(perturbed_grid_centers, n_circles))
+
+ # 3. Fill the rest of the population with random initializations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """
+ Selects two parents using tournament selection.
+ Randomly picks `tournament_size` individuals and selects the fittest among them.
+ """
+ # Select parent 1
+ tournament_candidates = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_candidates, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1 if possible
+ tournament_candidates_for_p2 = [ind for ind in tournament_candidates if ind != parent1]
+ if len(tournament_candidates_for_p2) > 0:
+ parent2 = max(tournament_candidates_for_p2, key=lambda ind: ind.fitness)
+ else: # Fallback: if only one distinct candidate (e.g., tournament_size=1, or all candidates were parent1)
+ parent2 = np.random.choice(population)
+ while parent2 == parent1: # Ensure different parents if possible
+ parent2 = np.random.choice(population)
+
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ for i in range(n_circles):
+ if np.random.rand() < 0.5: # 50% chance to inherit this circle's position from parent1
+ child_centers[i] = parent1.centers[i]
+ else: # Otherwise, inherit from parent2
+ child_centers[i] = parent2.centers[i]
+
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to ensure within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Mutates an individual's circle center positions.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ for i in range(n_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+ mutated_centers[i] = np.clip(mutated_centers[i], 0.0, 1.0) # Keep within bounds
+ return Individual(mutated_centers, n_circles)
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles in a unit square using a Genetic Algorithm
+ to maximize the sum of their radii.
+ """
+ n_circles = 26
+
+ # --- Genetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 150 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 2500 # Total number of evolutionary steps
+ MUTATION_RATE = 0.15 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.08 # Initial standard deviation of Gaussian noise for mutation
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 7 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 10 # Number of best individuals to directly carry over to next generation
+
+ # Radius calculation iterations: lower for speed during GA, higher for final precision
+ GA_RADIUS_ITER = 60 # Iterations for `compute_max_radii` during GA evolution
+ FINAL_RADIUS_ITER = 350 # Iterations for final best solution evaluation
+
+ # Initialize the first generation of candidate packings
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Evaluate fitness of the initial population
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ # Keep track of the best individual found across all generations
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child with reduced iterations for speed
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual if a better one is found
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Optional: Print progress
+ # if generation % 100 == 0:
+ # print(f"Generation {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final evaluation of the best individual with high `radius_iter` for maximum precision
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..66739ecfef4349ffe3d1dd59e485ff0ebbfac6e1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/edit.diff
@@ -0,0 +1,292 @@
+--- a/original.py
++++ b/original.py
+@@ -1,164 +1,199 @@
+ # EVOLVE-BLOCK-START
+ """
+-Improved physical simulation packing for n=26 circles.
+-This algorithm starts with a more symmetric and promising grid-based
+-configuration and uses an enhanced force model with "growth pressure"
+-to actively seek out more optimal arrangements.
++Physical simulation-based circle packing for n=26 circles.
++This algorithm is a crossover, combining the high-level strategy of the best-performing
++parent (multi-start, two-phase simulation, annealed growth pressure) with the
++efficient vectorized force calculations from the inspiration program.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles by starting with an interstitial
+- grid layout and then simulating physical forces to find an optimal
+- arrangement of centers.
++ Constructs a packing of 26 circles by simulating physical repulsion forces
++ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- # More iterations to allow the system to settle into a fine-tuned state.
+- iterations = 500
+- # A smaller learning rate is used for careful refinement of a good initial guess.
++ # Increased iterations due to vectorized efficiency
++ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+- # A growth factor to create a "pressure" for circles to expand,
+- # generating repulsive forces even when they are just touching.
+- growth_pressure = 1.005
++ initial_growth_pressure = 1.01
++ final_growth_pressure = 1.001
+
+- # --- Initialization ---
+- # Start with a 5x5 grid, a proven strong configuration.
+- centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- centers[k, 0] = (i + 0.5) * spacing
+- centers[k, 1] = (j + 0.5) * spacing
+- k += 1
++ best_sum_radii = -1.0
++ best_final_centers = None
+
+- # Place the 26th circle in the center of a grid cell. This is a
+- # more balanced starting point than a corner, as it doesn't
+- # immediately crowd any single circle. It mimics the known optimal
+- # structure for N=26.
+- centers[25] = [0.2, 0.2]
++ # Candidate initial positions for the 26th circle to optimize corner utilization.
++ candidate_extra_circle_initial_positions = [
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
++ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
++ ]
+
+- # No random noise is added. We want to deterministically refine this specific strong starting point.
++ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
++ # --- Initialization for current trial ---
++ current_centers = np.zeros((n, 2))
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for i in range(num_cells_side):
++ for j in range(num_cells_side):
++ current_centers[k, 0] = (i + 0.5) * spacing
++ current_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++ current_centers[25] = initial_pos_26th_circle
+
+- # --- Simulation Loop ---
+- for k_iter in range(iterations):
+- # 1. Calculate radii for the current center configuration
+- radii = compute_max_radii(centers)
++ centers_for_sim = current_centers.copy()
+
+- # 2. Calculate forces to push centers to a better configuration
+- forces = np.zeros((n, 2))
++ # --- Main Simulation Loop (Vectorized) ---
++ for sim_iter in range(iterations):
++ radii = compute_max_radii(centers_for_sim)
+
+- # Create "inflated" radii to generate repulsive forces even when just touching
+- inflated_radii = radii * growth_pressure
++ # Anneal growth pressure
++ current_growth_pressure = initial_growth_pressure - \
++ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
++ inflated_radii = radii * current_growth_pressure
+
+- # a. Circle-to-circle repulsion forces
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers[i] - centers[j]
+- dist = np.linalg.norm(vec)
+- if dist < 1e-9: continue
++ # --- Vectorized Force Calculation ---
++ # a. Circle-to-circle repulsion forces
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+- # Use inflated radii to calculate overlap, creating a growth pressure
+- overlap = inflated_radii[i] + inflated_radii[j] - dist
++ overlaps = np.maximum(0, sum_inflated_radii - dists)
++ np.fill_diagonal(overlaps, 0)
+
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
+
+- # b. Wall repulsion forces, also using inflated radii
+- for i in range(n):
+- # Left wall
+- overlap = inflated_radii[i] - centers[i, 0]
+- if overlap > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap
+- # Right wall
+- overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+- if overlap > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap
+- # Bottom wall
+- overlap = inflated_radii[i] - centers[i, 1]
+- if overlap > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap
+- # Top wall
+- overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+- if overlap > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
+
+- # 3. Update center positions based on forces
+- # We adjust the learning rate over time (annealing) to help the system settle
+- current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+- centers += forces * current_lr
++ # b. Vectorized Wall repulsion forces
++ wall_forces = np.zeros_like(centers_for_sim)
++ overlap_left = inflated_radii - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
++ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+- # 4. Enforce boundary constraints
+- centers = np.clip(centers, 0.0, 1.0)
++ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
++ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
++ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
++ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+- # --- Finalization ---
+- # After the simulation, do a final radius calculation on the optimized centers
+- final_centers = centers
++ forces = circle_forces + wall_forces
++
++ # --- Update center positions ---
++ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
++ centers_for_sim += forces * current_lr
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # --- Finalization for current trial ---
++ current_final_radii = compute_max_radii(centers_for_sim)
++ current_sum_radii = np.sum(current_final_radii)
++
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_final_centers = centers_for_sim.copy()
++
++ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
++ fine_tune_iterations = 100 # Increased due to efficiency
++ fine_tune_learning_rate = 0.001
++ centers_for_sim = best_final_centers.copy()
++
++ for _ in range(fine_tune_iterations):
++ radii = compute_max_radii(centers_for_sim)
++
++ # --- Vectorized Force Calculation (no growth pressure) ---
++ # a. Circle-to-circle forces
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
++
++ overlaps = np.maximum(0, sum_radii_pairs - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
++
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ # b. Wall forces
++ wall_forces = np.zeros_like(centers_for_sim)
++ overlap_left = radii - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
++ overlap_bottom = radii - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
++
++ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
++ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
++ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
++ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
++
++ forces = circle_forces + wall_forces
++
++ centers_for_sim += forces * fine_tune_learning_rate
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+- # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence.
+- for _ in range(250):
++ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+- if radii[i] + radii[j] > dist and dist > 1e-9:
++ if dist < 1e-9:
++ radii[i] = 0.0
++ radii[j] = 0.0
++ updated_in_pass = True
++ continue
++
++ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+- return np.maximum(radii, 0)
++ return np.maximum(radii, 0.0)
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..41788ebfff77a41763a8bd844dbf24483d23d2e7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/main.py
@@ -0,0 +1,199 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..4025a0eb85fb36c6bdf2411aa21c60171c4e402f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/original.py
@@ -0,0 +1,164 @@
+# EVOLVE-BLOCK-START
+"""
+Improved physical simulation packing for n=26 circles.
+This algorithm starts with a more symmetric and promising grid-based
+configuration and uses an enhanced force model with "growth pressure"
+to actively seek out more optimal arrangements.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by starting with an interstitial
+ grid layout and then simulating physical forces to find an optimal
+ arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # More iterations to allow the system to settle into a fine-tuned state.
+ iterations = 500
+ # A smaller learning rate is used for careful refinement of a good initial guess.
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ # A growth factor to create a "pressure" for circles to expand,
+ # generating repulsive forces even when they are just touching.
+ growth_pressure = 1.005
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, a proven strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the center of a grid cell. This is a
+ # more balanced starting point than a corner, as it doesn't
+ # immediately crowd any single circle. It mimics the known optimal
+ # structure for N=26.
+ centers[25] = [0.2, 0.2]
+
+ # No random noise is added. We want to deterministically refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Use inflated radii to calculate overlap, creating a growth pressure
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap = inflated_radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = inflated_radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e18911df85061d4d12efe40c487ccf73300b6085
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_39/search_replace.txt
@@ -0,0 +1,356 @@
+
+Revert to the 1.93-scoring crossover program implementation.
+The current code, while using a "growth pressure" concept, lacks several key features that were present in the higher-scoring "crossover" program:
+1. **Multi-Start Strategy**: The current code only tries one initial position for the 26th circle, whereas the crossover program explored multiple strategic starting points, which is crucial for escaping local minima in packing problems.
+2. **Annealed Growth Pressure**: The current code uses a fixed `growth_pressure`. The crossover program anneals this parameter, allowing for aggressive exploration initially and precise settling later.
+3. **Two-Phase Simulation**: The crossover program explicitly separates a main simulation phase from a fine-tuning phase, which further enhances precision.
+4. **Vectorized Force Calculation**: This is a critical performance improvement. The crossover program uses NumPy's vectorized operations for force calculations, enabling significantly more iterations (750 main + 100 fine-tune) than the current code's nested loops, leading to better convergence.
+5. **Robust `compute_max_radii` Iterations**: The crossover program uses 500 iterations for `compute_max_radii`, ensuring better convergence for the radii calculation itself.
+
+By reverting to the crossover program's structure and parameters, we re-incorporate these proven strategies, aiming to achieve at least the previous high score of 1.93 and set the stage for further enhancements.
+
+
+
+<<<<<<< SEARCH
+"""
+Improved physical simulation packing for n=26 circles.
+This algorithm starts with a more symmetric and promising grid-based
+configuration and uses an enhanced force model with "growth pressure"
+to actively seek out more optimal arrangements.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by starting with an interstitial
+ grid layout and then simulating physical forces to find an optimal
+ arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # More iterations to allow the system to settle into a fine-tuned state.
+ iterations = 500
+ # A smaller learning rate is used for careful refinement of a good initial guess.
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ # A growth factor to create a "pressure" for circles to expand,
+ # generating repulsive forces even when they are just touching.
+ growth_pressure = 1.005
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, a proven strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the center of a grid cell. This is a
+ # more balanced starting point than a corner, as it doesn't
+ # immediately crowd any single circle. It mimics the known optimal
+ # structure for N=26.
+ centers[25] = [0.2, 0.2]
+
+ # No random noise is added. We want to deterministically refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Use inflated radii to calculate overlap, creating a growth pressure
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap = inflated_radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = inflated_radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+=======
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7d3d01debd516f4616c3ead7b1af6b8fa99fad02
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..b9e3f19f42e50cad95054fd8bf23309e54ebaa31
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/edit.diff
@@ -0,0 +1,137 @@
+--- a/original.py
++++ b/original.py
+@@ -1,95 +1,116 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place 25 circles in a 5x5 grid
+ # This provides a more even distribution and better use of space
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+- # Place the 26th circle in a corner, slightly offset to allow for radius.
+- # This location is chosen to utilize corner space efficiently,
+- # as the grid circles are not directly in the corners.
+- centers[25] = [0.05, 0.05]
++ # Place the 26th circle in an interstitial gap within the 5x5 grid.
++ # The previous placement at [0.05, 0.05] caused significant overlap with existing
++ # grid circles, leading to sub-optimal radii.
++ # The point (0.2, 0.2) is chosen as it lies at the center of the square formed by
++ # four adjacent grid circles (0.1, 0.1), (0.3, 0.1), (0.1, 0.3), and (0.3, 0.3).
++ # This approach aims to utilize space more efficiently by filling a natural gap.
++ centers[25] = [0.2, 0.2]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+- # Then, limit by distance to other circles
+- # Each pair of circles with centers at distance d can have
+- # sum of radii at most d to avoid overlap
+- # This iterative scaling is a greedy approach and the order can matter,
+- # but for constructor-based approaches, it's a common method.
+- for _ in range(100): # Iterate multiple times to stabilize radii
++ # Then, iteratively adjust radii to prevent overlap while maximizing their size.
++ # This uses a relaxation method: each circle's radius is limited by its distance
++ # to boundaries and to the edge of every other circle.
++ # Iterate multiple times to stabilize radii and find a good local maximum.
++ for iteration in range(500): # Increased iterations for better convergence
++ radii_changed = False
++ # Create a copy of radii to store updates for this iteration,
++ # ensuring all updates are based on the same state at the start of the iteration.
++ current_radii_state = np.copy(radii)
++
+ for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
++ # Calculate potential maximum radius for circle i
++ # Limit by borders (re-evaluate in case a neighbor shrunk, allowing this one to grow)
++ max_r_i = min(centers[i][0], 1 - centers[i][0], centers[i][1], 1 - centers[i][1])
+
+- # If current radii would cause overlap
+- if radii[i] + radii[j] > dist:
+- # Scale both radii proportionally to just touch
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
++ # Limit by other circles
++ for j in range(n):
++ if i == j:
++ continue
++ dist_ij = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
++ # Circle i's radius cannot exceed dist_ij minus the current radius of j
++ max_r_i = min(max_r_i, dist_ij - current_radii_state[j])
++
++ # Ensure radius is non-negative
++ max_r_i = max(0.0, max_r_i)
++
++ # Update if there's a significant change
++ if abs(radii[i] - max_r_i) > 1e-7: # Use a small epsilon for float comparison
++ radii[i] = max_r_i
++ radii_changed = True
++
++ # If no significant changes occurred in this iteration, we've converged
++ if not radii_changed and iteration > 0:
++ break
+ return radii
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a42f44310766c6e2f524918207445cbcfc3074a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/main.py
@@ -0,0 +1,116 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place 25 circles in a 5x5 grid
+ # This provides a more even distribution and better use of space
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in an interstitial gap within the 5x5 grid.
+ # The previous placement at [0.05, 0.05] caused significant overlap with existing
+ # grid circles, leading to sub-optimal radii.
+ # The point (0.2, 0.2) is chosen as it lies at the center of the square formed by
+ # four adjacent grid circles (0.1, 0.1), (0.3, 0.1), (0.1, 0.3), and (0.3, 0.3).
+ # This approach aims to utilize space more efficiently by filling a natural gap.
+ centers[25] = [0.2, 0.2]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, iteratively adjust radii to prevent overlap while maximizing their size.
+ # This uses a relaxation method: each circle's radius is limited by its distance
+ # to boundaries and to the edge of every other circle.
+ # Iterate multiple times to stabilize radii and find a good local maximum.
+ for iteration in range(500): # Increased iterations for better convergence
+ radii_changed = False
+ # Create a copy of radii to store updates for this iteration,
+ # ensuring all updates are based on the same state at the start of the iteration.
+ current_radii_state = np.copy(radii)
+
+ for i in range(n):
+ # Calculate potential maximum radius for circle i
+ # Limit by borders (re-evaluate in case a neighbor shrunk, allowing this one to grow)
+ max_r_i = min(centers[i][0], 1 - centers[i][0], centers[i][1], 1 - centers[i][1])
+
+ # Limit by other circles
+ for j in range(n):
+ if i == j:
+ continue
+ dist_ij = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+ # Circle i's radius cannot exceed dist_ij minus the current radius of j
+ max_r_i = min(max_r_i, dist_ij - current_radii_state[j])
+
+ # Ensure radius is non-negative
+ max_r_i = max(0.0, max_r_i)
+
+ # Update if there's a significant change
+ if abs(radii[i] - max_r_i) > 1e-7: # Use a small epsilon for float comparison
+ radii[i] = max_r_i
+ radii_changed = True
+
+ # If no significant changes occurred in this iteration, we've converged
+ if not radii_changed and iteration > 0:
+ break
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..00c138de9a06c6d9335089fddee91aec818e5abd
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/original.py
@@ -0,0 +1,95 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place 25 circles in a 5x5 grid
+ # This provides a more even distribution and better use of space
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a corner, slightly offset to allow for radius.
+ # This location is chosen to utilize corner space efficiently,
+ # as the grid circles are not directly in the corners.
+ centers[25] = [0.05, 0.05]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ # This iterative scaling is a greedy approach and the order can matter,
+ # but for constructor-based approaches, it's a common method.
+ for _ in range(100): # Iterate multiple times to stabilize radii
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..2219471b6b95bd7d36b7cf5416aeb45dda3ee2f5
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": false,
+ "error": "Validation failed: Circles 0 & 25 overlap. Dist: 0.1414, Sum Radii: 0.2414"
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..d0ae04f5f936b57473fa6794dae1d6976440b673
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results
+Run 1/1 completed in 1.74 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/metrics.json
+Evaluation or Validation failed: Validation failed: Circles 0 & 25 overlap. Dist: 0.1414, Sum Radii: 0.2414
+Metrics:
+ combined_score: 3.4828427124746186
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1000, 0.9000)\n centers[5] = (0.3000, 0.1000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.7000, 0.9000)\n centers[20] = (0.9000, 0.1000)\n centers[21] = (0.9000, 0.3000)\n centers[22] = (0.9000, 0.5000)\n centers[23] = (0.9000, 0.7000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.2000, 0.2000)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 3.4828427124746186}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/packing_viz.png
+ execution_time_mean: 1.7360461950302124
+ execution_time_std: 0.0
+ num_valid_runs: 0
+ num_invalid_runs: 1
+ all_validation_errors: ['Circles 0 & 25 overlap. Dist: 0.1414, Sum Radii: 0.2414']
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..c035f59ca4a40cbe8331085e790ef45a1dc9f839
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/metrics.json
@@ -0,0 +1,18 @@
+{
+ "combined_score": 3.4828427124746186,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1000, 0.9000)\n centers[5] = (0.3000, 0.1000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.7000, 0.9000)\n centers[20] = (0.9000, 0.1000)\n centers[21] = (0.9000, 0.3000)\n centers[22] = (0.9000, 0.5000)\n centers[23] = (0.9000, 0.7000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.2000, 0.2000)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 3.4828427124746186
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/results/packing_viz.png",
+ "execution_time_mean": 1.7360461950302124,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 0,
+ "num_invalid_runs": 1,
+ "all_validation_errors": [
+ "Circles 0 & 25 overlap. Dist: 0.1414, Sum Radii: 0.2414"
+ ]
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..32cfca0a1c2f49569c0ecab5c629513eae36f136
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_4/search_replace.txt
@@ -0,0 +1,105 @@
+The current implementation, using a 5x5 grid for 25 circles and an additional 26th circle placed at `[0.05, 0.05]`, achieves a sum of radii of 1.87. The best known result is 2.635, indicating significant room for improvement.
+
+Upon analyzing the provided visualization and `centers_str` for the current program, several issues are apparent:
+1. **Problematic 26th circle placement**: The circle at `[0.05, 0.05]` is extremely close to the `[0.1, 0.1]` grid circle. This proximity forces both circles to have very small radii, effectively wasting space and reducing the overall sum.
+2. **Suboptimal `compute_max_radii` logic**: While the iterative proportional scaling in `compute_max_radii` is a valid heuristic, it might not converge to the globally optimal sum of radii for a fixed set of centers. A more robust iterative relaxation method is generally preferred.
+3. **Uneven radius distribution**: The visualization shows that circles in some parts of the grid are much smaller than others. This is partly due to the `[0.05, 0.05]` circle's influence and partly due to the `compute_max_radii` behavior.
+
+My proposed changes address these points by:
+1. **Re-positioning the 26th circle**: Instead of placing it in a detrimental corner location, it will be moved to a more strategic interstitial gap within the 5x5 grid, specifically at `[0.2, 0.2]`. This location is equidistant from four surrounding grid circles `(0.1, 0.1), (0.3, 0.1), (0.1, 0.3), (0.3, 0.3)`, allowing it to utilize space more efficiently without severely compromising its neighbors.
+2. **Refining the `compute_max_radii` function**: The current proportional scaling will be replaced with a standard iterative relaxation algorithm. This method ensures that each circle's radius is maximized based on both boundary constraints and the current radii of all other circles, leading to a more stable and potentially higher sum of radii. The number of iterations is increased to 500 to ensure better convergence.
+
+These combined changes aim to improve both the initial geometric placement and the subsequent radius calculation, leading to a higher sum of radii.
+
+---
+
+
+reposition_26th_circle
+
+
+
+The current placement of the 26th circle at `[0.05, 0.05]` in `construct_packing` is problematic. It's too close to the `(0.1, 0.1)` grid circle, leading to severe radius reduction for both and negatively impacting the overall sum of radii. To improve spatial efficiency, this change repositions the 26th circle to an interstitial gap within the 5x5 grid. The location `[0.2, 0.2]` is chosen because it lies symmetrically at the center of the square formed by four adjacent grid circles: `(0.1, 0.1), (0.3, 0.1), (0.1, 0.3), and (0.3, 0.3)`. This strategy aims to fill empty spaces more effectively, allowing the 26th circle to attain a reasonable radius without unduly shrinking its neighbors, thus increasing the total sum of radii.
+
+
+
+<<<<<<< SEARCH
+ # Place the 26th circle in a corner, slightly offset to allow for radius.
+ # This location is chosen to utilize corner space efficiently,
+ # as the grid circles are not directly in the corners.
+ centers[25] = [0.05, 0.05]
+=======
+ # Place the 26th circle in an interstitial gap within the 5x5 grid.
+ # The previous placement at [0.05, 0.05] caused significant overlap with existing
+ # grid circles, leading to sub-optimal radii.
+ # The point (0.2, 0.2) is chosen as it lies at the center of the square formed by
+ # four adjacent grid circles (0.1, 0.1), (0.3, 0.1), (0.1, 0.3), and (0.3, 0.3).
+ # This approach aims to utilize space more efficiently by filling a natural gap.
+ centers[25] = [0.2, 0.2]
+>>>>>>> REPLACE
+
+
+
+refine_compute_max_radii
+
+
+
+The current `compute_max_radii` function uses a proportional scaling approach to resolve overlaps, which can be sensitive to the order of operations and may not always yield the maximum possible sum of radii for a given set of fixed centers. This change replaces it with a more robust iterative relaxation method. In this refined approach, each circle's radius is iteratively updated to the maximum possible value, constrained by both the unit square boundaries and the current radii of all other circles. This process is repeated for a higher number of iterations (500) to ensure better convergence, which is crucial for achieving a locally optimal packing configuration and maximizing the total sum of radii. The updates are applied simultaneously for each iteration to improve stability.
+
+
+
+<<<<<<< SEARCH
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ # This iterative scaling is a greedy approach and the order can matter,
+ # but for constructor-based approaches, it's a common method.
+ for _ in range(100): # Iterate multiple times to stabilize radii
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ return radii
+=======
+ # Then, iteratively adjust radii to prevent overlap while maximizing their size.
+ # This uses a relaxation method: each circle's radius is limited by its distance
+ # to boundaries and to the edge of every other circle.
+ # Iterate multiple times to stabilize radii and find a good local maximum.
+ for iteration in range(500): # Increased iterations for better convergence
+ radii_changed = False
+ # Create a copy of radii to store updates for this iteration,
+ # ensuring all updates are based on the same state at the start of the iteration.
+ current_radii_state = np.copy(radii)
+
+ for i in range(n):
+ # Calculate potential maximum radius for circle i
+ # Limit by borders (re-evaluate in case a neighbor shrunk, allowing this one to grow)
+ max_r_i = min(centers[i][0], 1 - centers[i][0], centers[i][1], 1 - centers[i][1])
+
+ # Limit by other circles
+ for j in range(n):
+ if i == j:
+ continue
+ dist_ij = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+ # Circle i's radius cannot exceed dist_ij minus the current radius of j
+ max_r_i = min(max_r_i, dist_ij - current_radii_state[j])
+
+ # Ensure radius is non-negative
+ max_r_i = max(0.0, max_r_i)
+
+ # Update if there's a significant change
+ if abs(radii[i] - max_r_i) > 1e-7: # Use a small epsilon for float comparison
+ radii[i] = max_r_i
+ radii_changed = True
+
+ # If no significant changes occurred in this iteration, we've converged
+ if not radii_changed and iteration > 0:
+ break
+ return radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f5866e55986dd6526c6855a2d6f54d9242371333
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..e358ce3ed3eab8d9bef0491ff54d6840764551aa
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/edit.diff
@@ -0,0 +1,341 @@
+--- a/original.py
++++ b/original.py
+@@ -1,256 +1,309 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+- simulation_iterations = 500 # Number of steps for force-directed simulation
+- initial_learning_rate = 0.02 # A more stable learning rate for refinement.
+- wall_repulsion_strength = 0.8 # Stronger walls to utilize boundary space.
+- growth_pressure = 1.005 # Key parameter to create expansion force.
++ simulation_iterations = 1000 # Increased iterations for thorough refinement.
++ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
++ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
++ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
++ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
++ fine_tune_iter = 200 # Increased iterations for final settlement.
++ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+- # --- Physical Simulation Loop (Force-Directed Relaxation) ---
++ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
++ # Anneal growth pressure quadratically.
++ progress = sim_iter / simulation_iterations
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+- # 1.5. Artificially inflate radii to create an expansion 'pressure'.
+- # This is the crucial step that drives the simulation.
+- pressured_radii = radii_for_sim * growth_pressure
+-
+- # 2. Calculate forces to push centers to a better configuration
++ # 2. Artificially inflate radii to create an expansion 'pressure'.
++ pressured_radii = radii_for_sim * current_growth_pressure
++
++ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+- # a. Circle-to-circle repulsion forces (based on pressured radii)
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers_for_sim[i] - centers_for_sim[j]
+- dist = np.linalg.norm(vec)
+-
+- if dist < 1e-9: # Centers are too close.
+- continue
+-
+- # Calculate overlap based on the artificially inflated radii.
+- overlap = pressured_radii[i] + pressured_radii[j] - dist
+-
+- if overlap > 0:
+- # Force magnitude proportional to overlap
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec # Equal and opposite reaction
++ # a) Circle-to-circle repulsion (vectorized)
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++
++ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
++
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = radii_sums - dists
++ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
++ np.fill_diagonal(overlaps, 0) # No self-repulsion
++
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+- # After simulation, use the refined centers to compute FINAL, accurate radii
++ # --- Fine-Tuning Loop ---
++ # This second phase has no growth pressure. It allows the packing to settle
++ # and resolve minor overlaps, leading to a more precise final state.
++ for _ in range(fine_tune_iter):
++ # 1. Calculate radii for the current positions. NO growth pressure.
++ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
++
++ forces = np.zeros((n, 2))
++
++ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
++ # a) Circle-to-circle repulsion (vectorized)
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
++ overlaps = radii_sums - dists
++ overlaps[overlaps < 0] = 0.0
++ np.fill_diagonal(overlaps, 0)
++
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ # b. Wall repulsion forces (based on actual radii)
++ for i in range(n):
++ # Left wall
++ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
++ if overlap_left > 0:
++ forces[i, 0] += wall_repulsion_strength * overlap_left
++ # Right wall
++ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
++ if overlap_right > 0:
++ forces[i, 0] -= wall_repulsion_strength * overlap_right
++ # Bottom wall
++ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
++ if overlap_bottom > 0:
++ forces[i, 1] += wall_repulsion_strength * overlap_bottom
++ # Top wall
++ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
++ if overlap_top > 0:
++ forces[i, 1] -= wall_repulsion_strength * overlap_top
++
++ # 3. Update positions with a small, constant learning rate.
++ centers_for_sim += forces * fine_tune_lr
++
++ # 4. Enforce hard boundary constraints.
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++
++ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+ def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fc8c7dbfd5b6e3983eb39b62837085607d66e9d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/main.py
@@ -0,0 +1,309 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 1000 # Increased iterations for thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 200 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (based on actual radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b901cf614ca947e5bc73a16b33ef8ab284cd09e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/original.py
@@ -0,0 +1,256 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+ initial_learning_rate = 0.02 # A more stable learning rate for refinement.
+ wall_repulsion_strength = 0.8 # Stronger walls to utilize boundary space.
+ growth_pressure = 1.005 # Key parameter to create expansion force.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 1.5. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the crucial step that drives the simulation.
+ pressured_radii = radii_for_sim * growth_pressure
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ # Calculate overlap based on the artificially inflated radii.
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..680260458b5e3510ade3cf98d814e3c7b2fec0df
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results
+Run 1/1 completed in 328.87 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9454836647028526
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3029, 0.0971)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8990, 0.1010)\n centers[5] = (0.0918, 0.3082)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.4861, 0.6984)\n centers[18] = (0.6994, 0.6893)\n centers[19] = (0.9094, 0.7000)\n centers[20] = (0.0945, 0.8996)\n centers[21] = (0.2951, 0.8939)\n centers[22] = (0.5054, 0.8959)\n centers[23] = (0.7049, 0.9046)\n centers[24] = (0.9003, 0.8985)\n centers[25] = (0.2053, 0.1947)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9454836647028526}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/packing_viz.png
+ execution_time_mean: 328.8693988248706
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..295ac2e4bb936927b64f7c73e95cf154aa59c075
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9454836647028526,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3029, 0.0971)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8990, 0.1010)\n centers[5] = (0.0918, 0.3082)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.4861, 0.6984)\n centers[18] = (0.6994, 0.6893)\n centers[19] = (0.9094, 0.7000)\n centers[20] = (0.0945, 0.8996)\n centers[21] = (0.2951, 0.8939)\n centers[22] = (0.5054, 0.8959)\n centers[23] = (0.7049, 0.9046)\n centers[24] = (0.9003, 0.8985)\n centers[25] = (0.2053, 0.1947)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9454836647028526
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/results/packing_viz.png",
+ "execution_time_mean": 328.8693988248706,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c78ae138a23ed0412257a3fb701750969d69225a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_40/search_replace.txt
@@ -0,0 +1,225 @@
+
+
+This edit reintroduces the annealed growth pressure mechanism and a dedicated fine-tuning phase, which were present in higher-scoring prior programs (e.g., the class-based packer achieving 1.95 score). The current program simplified these aspects, leading to a slight performance regression (1.93 score).
+
+1. **Annealed Growth Pressure**: Instead of a constant `growth_pressure`, the simulation now uses `initial_growth_pressure` and `final_growth_pressure` with a quadratic annealing schedule. This allows for aggressive "unfreezing" of the initial grid, followed by a more gentle refinement, which is effective at escaping local minima and finding denser packings.
+2. **Fine-Tuning Phase**: A separate loop is added after the main simulation. This phase runs without growth pressure and with a very small, constant learning rate. Its purpose is to allow the packing to settle precisely and resolve any minor residual overlaps after the more aggressive, pressure-driven main simulation.
+3. **Vectorized Force Calculation**: The circle-to-circle repulsion force calculation is vectorized using NumPy operations. This significantly improves performance compared to nested Python loops, especially as the number of circles increases, and was present in higher-scoring class-based programs. This optimization is applied to both the main simulation and the fine-tuning phase.
+4. **Updated Hyperparameters**: The `simulation_iterations` and `fine_tune_iter` values are increased, mirroring the successful configuration of the 1.95-scoring class-based program, to allow more time for the improved simulation dynamics to converge.
+
+
+<<<<<<< SEARCH
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+ initial_learning_rate = 0.02 # A more stable learning rate for refinement.
+ wall_repulsion_strength = 0.8 # Stronger walls to utilize boundary space.
+ growth_pressure = 1.005 # Key parameter to create expansion force.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 1.5. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the crucial step that drives the simulation.
+ pressured_radii = radii_for_sim * growth_pressure
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ # Calculate overlap based on the artificially inflated radii.
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+=======
+ simulation_iterations = 1000 # Increased iterations for thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 200 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (based on actual radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..48a313d71e849e4a625216aa6bb4e7406d8a1569
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/edit.diff
@@ -0,0 +1,219 @@
+--- a/original.py
++++ b/original.py
+@@ -1,104 +1,215 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+- # Compute maximum valid radii for this superior configuration.
++ # Refine the strong initial grid using a force-directed simulation. This allows
++ # the circles to shift from the rigid grid into a more optimal packing.
++ centers = refine_centers_with_simulation(centers)
++
++ # Compute maximum valid radii for the refined configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
++
++
++def refine_centers_with_simulation(centers):
++ """
++ Refines circle centers using a two-phase force-directed simulation inspired
++ by high-performance prior implementations. It first uses an annealed 'growth
++ pressure' to explore the solution space, then fine-tunes the positions.
++ """
++ n = centers.shape[0]
++
++ # --- Hyperparameters from best-performing prior program ---
++ sim_iter = 1000
++ learning_rate = 0.02
++ wall_strength = 0.8
++ initial_growth_pressure = 1.02
++ final_growth_pressure = 1.001
++ fine_tune_iter = 200
++ fine_tune_lr = 0.001
++
++ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
++ for i_iter in range(sim_iter):
++ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
++ progress = i_iter / sim_iter
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++
++ # 1. Calculate radii for current positions
++ current_radii = compute_max_radii(centers)
++
++ # 2. Artificially inflate radii to create expansion 'pressure'
++ pressured_radii = current_radii * current_growth_pressure
++
++ forces = np.zeros_like(centers)
++
++ # 3. Calculate repulsion forces based on artificial overlaps
++ # a) Vectorized Circle-to-circle repulsion
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
++
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = radii_sums - dists
++ overlaps[overlaps < 0] = 0.0
++ np.fill_diagonal(overlaps, 0)
++
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ # b) Wall repulsion
++ for i in range(n):
++ overlap_l = pressured_radii[i] - centers[i, 0]
++ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
++ overlap_r = (centers[i, 0] + pressured_radii[i]) - 1.0
++ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
++ overlap_b = pressured_radii[i] - centers[i, 1]
++ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
++ overlap_t = (centers[i, 1] + pressured_radii[i]) - 1.0
++ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
++
++ # 4. Update positions with an annealing learning rate for stability
++ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
++ centers += forces * lr
++
++ # 5. Enforce hard boundary constraints
++ centers = np.clip(centers, 0.0, 1.0)
++
++ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
++ for _ in range(fine_tune_iter):
++ # 1. Calculate radii based on actual positions
++ current_radii = compute_max_radii(centers)
++
++ forces = np.zeros_like(centers)
++
++ # 2. Calculate repulsion forces based on *actual* overlaps
++ # a) Vectorized Circle-to-circle repulsion
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
++ overlaps = radii_sums - dists
++ overlaps[overlaps < 0] = 0.0
++ np.fill_diagonal(overlaps, 0)
++
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ # b) Wall repulsion (based on actual radii)
++ for i in range(n):
++ overlap_l = current_radii[i] - centers[i, 0]
++ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
++ overlap_r = (centers[i, 0] + current_radii[i]) - 1.0
++ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
++ overlap_b = current_radii[i] - centers[i, 1]
++ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
++ overlap_t = (centers[i, 1] + current_radii[i]) - 1.0
++ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
++
++ # 3. Update positions with a small, constant learning rate to settle
++ centers += forces * fine_tune_lr
++
++ # 4. Enforce hard boundary constraints
++ centers = np.clip(centers, 0.0, 1.0)
++
++ return centers
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # Increased iterations to ensure full convergence for a dense packing.
+ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..e5fee9675192b2a1a8fa9f1ebbf1408780c38676
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/main.py
@@ -0,0 +1,215 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Refine the strong initial grid using a force-directed simulation. This allows
+ # the circles to shift from the rigid grid into a more optimal packing.
+ centers = refine_centers_with_simulation(centers)
+
+ # Compute maximum valid radii for the refined configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def refine_centers_with_simulation(centers):
+ """
+ Refines circle centers using a two-phase force-directed simulation inspired
+ by high-performance prior implementations. It first uses an annealed 'growth
+ pressure' to explore the solution space, then fine-tunes the positions.
+ """
+ n = centers.shape[0]
+
+ # --- Hyperparameters from best-performing prior program ---
+ sim_iter = 1000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 200
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for current positions
+ current_radii = compute_max_radii(centers)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion
+ for i in range(n):
+ overlap_l = pressured_radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+ overlap_r = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+ overlap_b = pressured_radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+ overlap_t = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii based on actual positions
+ current_radii = compute_max_radii(centers)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(n):
+ overlap_l = current_radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+ overlap_r = (centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+ overlap_b = current_radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+ overlap_t = (centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # Increased iterations to ensure full convergence for a dense packing.
+ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d2a47b71e26ff4a20c43db885428c2c8b80ec3da
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/original.py
@@ -0,0 +1,104 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Compute maximum valid radii for this superior configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # Increased iterations to ensure full convergence for a dense packing.
+ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9a0ab63b4c1f0df4778a37ec6cd607afefb05482
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_41/search_replace.txt
@@ -0,0 +1,142 @@
+
+reinstate_force_simulation
+
+
+The current program uses a static grid-based initialization but performs no subsequent optimization of the circle centers. This results in a very suboptimal packing, as shown by the low score (1.92) and the visualization where circles are far apart.
+
+The high-scoring prior programs (scores > 1.93) all use a sophisticated force-directed simulation to refine the initial positions. The most successful versions use a two-phase approach:
+1. A refinement phase with an annealed "growth pressure" to aggressively explore for better configurations.
+2. A fine-tuning phase with no pressure to allow the packing to settle into a precise, valid state.
+
+This edit reintroduces this critical refinement logic. I am adding a new function, `refine_centers_with_simulation`, which encapsulates the complete two-phase simulation logic, including vectorized force calculations, annealed growth pressure, and a final fine-tuning stage. The hyperparameters are borrowed directly from the best-performing prior program (score 1.95).
+
+By calling this function after initialization and before the final radius calculation, the program will now actively optimize the circle packing instead of relying on a static initial guess, which is expected to significantly improve the sum of radii.
+
+
+<<<<<<< SEARCH
+ # Compute maximum valid radii for this superior configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+=======
+ # Refine the strong initial grid using a force-directed simulation. This allows
+ # the circles to shift from the rigid grid into a more optimal packing.
+ centers = refine_centers_with_simulation(centers)
+
+ # Compute maximum valid radii for the refined configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def refine_centers_with_simulation(centers):
+ """
+ Refines circle centers using a two-phase force-directed simulation inspired
+ by high-performance prior implementations. It first uses an annealed 'growth
+ pressure' to explore the solution space, then fine-tunes the positions.
+ """
+ n = centers.shape[0]
+
+ # --- Hyperparameters from best-performing prior program ---
+ sim_iter = 1000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 200
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for current positions
+ current_radii = compute_max_radii(centers)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion
+ for i in range(n):
+ overlap_l = pressured_radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+ overlap_r = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+ overlap_b = pressured_radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+ overlap_t = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii based on actual positions
+ current_radii = compute_max_radii(centers)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(n):
+ overlap_l = current_radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+ overlap_r = (centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+ overlap_b = current_radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+ overlap_t = (centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers):
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a8d8fd284e3680102641edbe76a35ad8e55b9f89
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..2a33dda8615bf1ef92b2aaf5c3bbce82d0628948
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/edit.diff
@@ -0,0 +1,317 @@
+--- a/original.py
++++ b/original.py
+@@ -1,164 +1,186 @@
+ # EVOLVE-BLOCK-START
+ """
+-Improved physical simulation packing for n=26 circles.
+-This algorithm starts with a more symmetric and promising grid-based
+-configuration and uses an enhanced force model with "growth pressure"
+-to actively seek out more optimal arrangements.
++Object-oriented physical simulation for n=26 circles.
++This version refactors the simulation into a SimulationManager class,
++encapsulating state, hyperparameters, and logic into a cohesive unit.
++This structure improves clarity, maintainability, and facilitates the
++implementation of complex, multi-phase optimization strategies.
+ """
+
+ import numpy as np
+
+-def construct_packing():
+- """
+- Constructs a packing of 26 circles by starting with an interstitial
+- grid layout and then simulating physical forces to find an optimal
+- arrangement of centers.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- n = 26
+-
+- # --- Hyperparameters for the simulation ---
+- # More iterations to allow the system to settle into a fine-tuned state.
+- iterations = 500
+- # A smaller learning rate is used for careful refinement of a good initial guess.
+- learning_rate = 0.01
+- wall_repulsion_strength = 0.5
+- # A growth factor to create a "pressure" for circles to expand,
+- # generating repulsive forces even when they are just touching.
+- growth_pressure = 1.005
+-
+- # --- Initialization ---
+- # Start with a 5x5 grid, a proven strong configuration.
+- centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- centers[k, 0] = (i + 0.5) * spacing
+- centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- # Place the 26th circle in the center of a grid cell. This is a
+- # more balanced starting point than a corner, as it doesn't
+- # immediately crowd any single circle. It mimics the known optimal
+- # structure for N=26.
+- centers[25] = [0.2, 0.2]
+-
+- # No random noise is added. We want to deterministically refine this specific strong starting point.
+-
+- # --- Simulation Loop ---
+- for k_iter in range(iterations):
+- # 1. Calculate radii for the current center configuration
+- radii = compute_max_radii(centers)
+-
+- # 2. Calculate forces to push centers to a better configuration
+- forces = np.zeros((n, 2))
+-
+- # Create "inflated" radii to generate repulsive forces even when just touching
+- inflated_radii = radii * growth_pressure
+-
+- # a. Circle-to-circle repulsion forces
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers[i] - centers[j]
+- dist = np.linalg.norm(vec)
+- if dist < 1e-9: continue
+-
+- # Use inflated radii to calculate overlap, creating a growth pressure
+- overlap = inflated_radii[i] + inflated_radii[j] - dist
+-
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec
+-
+- # b. Wall repulsion forces, also using inflated radii
+- for i in range(n):
+- # Left wall
+- overlap = inflated_radii[i] - centers[i, 0]
+- if overlap > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap
+- # Right wall
+- overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+- if overlap > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap
+- # Bottom wall
+- overlap = inflated_radii[i] - centers[i, 1]
+- if overlap > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap
+- # Top wall
+- overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+- if overlap > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap
+-
+- # 3. Update center positions based on forces
+- # We adjust the learning rate over time (annealing) to help the system settle
+- current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+- centers += forces * current_lr
+-
+- # 4. Enforce boundary constraints
+- centers = np.clip(centers, 0.0, 1.0)
+-
+- # --- Finalization ---
+- # After the simulation, do a final radius calculation on the optimized centers
+- final_centers = centers
+- final_radii = compute_max_radii(final_centers)
+-
+- return final_centers, final_radii
+-
+-
+ def compute_max_radii(centers):
+ """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This uses an iterative proportional scaling method.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
++ Compute the maximum possible radii for given center positions.
++ This is a stateless helper function using iterative proportional scaling.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
++ # Initial radii are limited by distance to the walls of the unit square.
++ radii = np.minimum(radii, centers[:, 0])
++ radii = np.minimum(radii, 1 - centers[:, 0])
++ radii = np.minimum(radii, centers[:, 1])
++ radii = np.minimum(radii, 1 - centers[:, 1])
+
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence.
+- for _ in range(250):
++ # Iteratively adjust radii based on distances between circle pairs.
++ # A high number of iterations ensures convergence to a stable state.
++ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+- if radii[i] + radii[j] > dist and dist > 1e-9:
++ if dist < 1e-9: # Robustly handle coincident centers
++ radii[i] = 0.0
++ radii[j] = 0.0
+ updated_in_pass = True
++ continue
++
++ if radii[i] + radii[j] > dist:
++ updated_in_pass = True
++ # Scale down radii proportionally to resolve overlap
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+-
++
+ if not updated_in_pass:
+ break
++
++ return np.maximum(radii, 0.0)
++
++class SimulationManager:
++ """
++ Manages the entire circle packing simulation process.
++ It encapsulates hyperparameters, state, and orchestrates the multi-phase optimization.
++ """
++ def __init__(self, n=26):
++ self.n = n
++ # Hyperparameters
++ self.main_iterations = 1000
++ self.main_learning_rate = 0.01
++ self.initial_growth_pressure = 1.015
++ self.final_growth_pressure = 1.0005
++
++ self.finetune_iterations = 250
++ self.finetune_learning_rate = 0.001
++
++ self.wall_repulsion_strength = 0.25
++
++ # State
++ self.best_sum_radii = -1.0
++ self.best_final_centers = None
++
++ def _initialize_centers(self, extra_circle_pos):
++ """Creates the initial 5x5 grid and places the 26th circle."""
++ centers = np.zeros((self.n, 2))
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for i in range(num_cells_side):
++ for j in range(num_cells_side):
++ centers[k, 0] = (i + 0.5) * spacing
++ centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++ centers[25] = extra_circle_pos
++ return centers
++
++ def _calculate_forces(self, centers, radii, growth_pressure_factor):
++ """Calculates repulsion forces using efficient vectorized operations."""
++ forces = np.zeros_like(centers)
++ inflated_radii = radii * growth_pressure_factor
++
++ # Vectorized circle-to-circle repulsion
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
++
++ overlaps = np.maximum(0, sum_inflated_radii - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
++
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ # Vectorized wall repulsion
++ wall_forces = np.zeros_like(centers)
++ wall_forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 0])
++ wall_forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 0] + inflated_radii) - 1.0)
++ wall_forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 1])
++ wall_forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 1] + inflated_radii) - 1.0)
++ forces += wall_forces
++
++ return forces
++
++ def _run_simulation_phase(self, centers, iterations, lr, initial_pressure, final_pressure):
++ """Runs a single phase of the simulation with annealed parameters."""
++ for sim_iter in range(iterations):
++ radii = compute_max_radii(centers)
+
+- return np.maximum(radii, 0)
++ # Anneal growth pressure (linear decay)
++ pressure_factor = initial_pressure - (initial_pressure - final_pressure) * (sim_iter / iterations)
++
++ forces = self._calculate_forces(centers, radii, pressure_factor)
++
++ # Anneal learning rate (quadratic decay)
++ current_lr = lr * (1.0 - (sim_iter / iterations))**2
++
++ centers += forces * current_lr
++ centers = np.clip(centers, 0.0, 1.0)
++ return centers
++
++ def solve(self):
++ """Orchestrates the full optimization process and returns the best result."""
++ # Multi-start strategy: test different initial positions for the 26th circle.
++ candidate_positions = [
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
++ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial
++ ]
++
++ for initial_pos in candidate_positions:
++ # --- Main Exploration Phase ---
++ initial_centers = self._initialize_centers(initial_pos)
++ main_phase_centers = self._run_simulation_phase(
++ initial_centers, self.main_iterations, self.main_learning_rate,
++ self.initial_growth_pressure, self.final_growth_pressure
++ )
++
++ # --- Fine-Tuning Phase ---
++ # This phase uses a small learning rate and no growth pressure for precision.
++ final_centers_trial = self._run_simulation_phase(
++ main_phase_centers, self.finetune_iterations, self.finetune_learning_rate,
++ 1.0, 1.0 # No growth pressure
++ )
++
++ # Check if this trial yielded a better result
++ current_sum_radii = np.sum(compute_max_radii(final_centers_trial))
++ if current_sum_radii > self.best_sum_radii:
++ self.best_sum_radii = current_sum_radii
++ self.best_final_centers = final_centers_trial.copy()
++
++ final_radii = compute_max_radii(self.best_final_centers)
++ return self.best_final_centers, final_radii
++
++
++def construct_packing():
++ """
++ Constructs a packing of 26 circles by instantiating and running the SimulationManager.
++ This function acts as a clean entry point to the object-oriented simulation logic.
++ """
++ manager = SimulationManager(n=26)
++ centers, radii = manager.solve()
++ return centers, radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..39efca572d436fd78bdf5a47e6b486dee4a62300
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/main.py
@@ -0,0 +1,186 @@
+# EVOLVE-BLOCK-START
+"""
+Object-oriented physical simulation for n=26 circles.
+This version refactors the simulation into a SimulationManager class,
+encapsulating state, hyperparameters, and logic into a cohesive unit.
+This structure improves clarity, maintainability, and facilitates the
+implementation of complex, multi-phase optimization strategies.
+"""
+
+import numpy as np
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for given center positions.
+ This is a stateless helper function using iterative proportional scaling.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # Initial radii are limited by distance to the walls of the unit square.
+ radii = np.minimum(radii, centers[:, 0])
+ radii = np.minimum(radii, 1 - centers[:, 0])
+ radii = np.minimum(radii, centers[:, 1])
+ radii = np.minimum(radii, 1 - centers[:, 1])
+
+ # Iteratively adjust radii based on distances between circle pairs.
+ # A high number of iterations ensures convergence to a stable state.
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Robustly handle coincident centers
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale down radii proportionally to resolve overlap
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+class SimulationManager:
+ """
+ Manages the entire circle packing simulation process.
+ It encapsulates hyperparameters, state, and orchestrates the multi-phase optimization.
+ """
+ def __init__(self, n=26):
+ self.n = n
+ # Hyperparameters
+ self.main_iterations = 1000
+ self.main_learning_rate = 0.01
+ self.initial_growth_pressure = 1.015
+ self.final_growth_pressure = 1.0005
+
+ self.finetune_iterations = 250
+ self.finetune_learning_rate = 0.001
+
+ self.wall_repulsion_strength = 0.25
+
+ # State
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_circle_pos):
+ """Creates the initial 5x5 grid and places the 26th circle."""
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_circle_pos
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """Calculates repulsion forces using efficient vectorized operations."""
+ forces = np.zeros_like(centers)
+ inflated_radii = radii * growth_pressure_factor
+
+ # Vectorized circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ wall_forces = np.zeros_like(centers)
+ wall_forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 0])
+ wall_forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 1])
+ wall_forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 1] + inflated_radii) - 1.0)
+ forces += wall_forces
+
+ return forces
+
+ def _run_simulation_phase(self, centers, iterations, lr, initial_pressure, final_pressure):
+ """Runs a single phase of the simulation with annealed parameters."""
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers)
+
+ # Anneal growth pressure (linear decay)
+ pressure_factor = initial_pressure - (initial_pressure - final_pressure) * (sim_iter / iterations)
+
+ forces = self._calculate_forces(centers, radii, pressure_factor)
+
+ # Anneal learning rate (quadratic decay)
+ current_lr = lr * (1.0 - (sim_iter / iterations))**2
+
+ centers += forces * current_lr
+ centers = np.clip(centers, 0.0, 1.0)
+ return centers
+
+ def solve(self):
+ """Orchestrates the full optimization process and returns the best result."""
+ # Multi-start strategy: test different initial positions for the 26th circle.
+ candidate_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial
+ ]
+
+ for initial_pos in candidate_positions:
+ # --- Main Exploration Phase ---
+ initial_centers = self._initialize_centers(initial_pos)
+ main_phase_centers = self._run_simulation_phase(
+ initial_centers, self.main_iterations, self.main_learning_rate,
+ self.initial_growth_pressure, self.final_growth_pressure
+ )
+
+ # --- Fine-Tuning Phase ---
+ # This phase uses a small learning rate and no growth pressure for precision.
+ final_centers_trial = self._run_simulation_phase(
+ main_phase_centers, self.finetune_iterations, self.finetune_learning_rate,
+ 1.0, 1.0 # No growth pressure
+ )
+
+ # Check if this trial yielded a better result
+ current_sum_radii = np.sum(compute_max_radii(final_centers_trial))
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = final_centers_trial.copy()
+
+ final_radii = compute_max_radii(self.best_final_centers)
+ return self.best_final_centers, final_radii
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by instantiating and running the SimulationManager.
+ This function acts as a clean entry point to the object-oriented simulation logic.
+ """
+ manager = SimulationManager(n=26)
+ centers, radii = manager.solve()
+ return centers, radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa667478dee645e6313153e219f5465b2e4e975a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/original.py
@@ -0,0 +1,164 @@
+# EVOLVE-BLOCK-START
+"""
+Improved physical simulation packing for n=26 circles.
+This algorithm starts with a more symmetric and promising grid-based
+configuration and uses an enhanced force model with "growth pressure"
+to actively seek out more optimal arrangements.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by starting with an interstitial
+ grid layout and then simulating physical forces to find an optimal
+ arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # More iterations to allow the system to settle into a fine-tuned state.
+ iterations = 500
+ # A smaller learning rate is used for careful refinement of a good initial guess.
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ # A growth factor to create a "pressure" for circles to expand,
+ # generating repulsive forces even when they are just touching.
+ growth_pressure = 1.005
+
+ # --- Initialization ---
+ # Start with a 5x5 grid, a proven strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the center of a grid cell. This is a
+ # more balanced starting point than a corner, as it doesn't
+ # immediately crowd any single circle. It mimics the known optimal
+ # structure for N=26.
+ centers[25] = [0.2, 0.2]
+
+ # No random noise is added. We want to deterministically refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k_iter in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # Create "inflated" radii to generate repulsive forces even when just touching
+ inflated_radii = radii * growth_pressure
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ # Use inflated radii to calculate overlap, creating a growth pressure
+ overlap = inflated_radii[i] + inflated_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using inflated radii
+ for i in range(n):
+ # Left wall
+ overlap = inflated_radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = inflated_radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + inflated_radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..b5a69179dda4ac202f1fec429da353155894bf5a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results
+Run 1/1 completed in 24.40 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.933497329605997
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1005, 0.8995)\n centers[5] = (0.3039, 0.0961)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7001, 0.4958)\n centers[18] = (0.6950, 0.6982)\n centers[19] = (0.7001, 0.9054)\n centers[20] = (0.8999, 0.0983)\n centers[21] = (0.8990, 0.2989)\n centers[22] = (0.8990, 0.5022)\n centers[23] = (0.9041, 0.7010)\n centers[24] = (0.8993, 0.8996)\n centers[25] = (0.1961, 0.2039)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.933497329605997}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/packing_viz.png
+ execution_time_mean: 24.396810163743794
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..e5492075f73dad585c79fcfaf8efd1dbfabb4f8f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.933497329605997,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1005, 0.8995)\n centers[5] = (0.3039, 0.0961)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7001, 0.4958)\n centers[18] = (0.6950, 0.6982)\n centers[19] = (0.7001, 0.9054)\n centers[20] = (0.8999, 0.0983)\n centers[21] = (0.8990, 0.2989)\n centers[22] = (0.8990, 0.5022)\n centers[23] = (0.9041, 0.7010)\n centers[24] = (0.8993, 0.8996)\n centers[25] = (0.1961, 0.2039)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.933497329605997
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/results/packing_viz.png",
+ "execution_time_mean": 24.396810163743794,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2f85bac18652410de23d27363a7d0d71503f9bf9
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_42/rewrite.txt
@@ -0,0 +1,177 @@
+# EVOLVE-BLOCK-START
+"""
+Object-oriented physical simulation for n=26 circles.
+This version refactors the simulation into a SimulationManager class,
+encapsulating state, hyperparameters, and logic into a cohesive unit.
+This structure improves clarity, maintainability, and facilitates the
+implementation of complex, multi-phase optimization strategies.
+"""
+
+import numpy as np
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for given center positions.
+ This is a stateless helper function using iterative proportional scaling.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # Initial radii are limited by distance to the walls of the unit square.
+ radii = np.minimum(radii, centers[:, 0])
+ radii = np.minimum(radii, 1 - centers[:, 0])
+ radii = np.minimum(radii, centers[:, 1])
+ radii = np.minimum(radii, 1 - centers[:, 1])
+
+ # Iteratively adjust radii based on distances between circle pairs.
+ # A high number of iterations ensures convergence to a stable state.
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Robustly handle coincident centers
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale down radii proportionally to resolve overlap
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+class SimulationManager:
+ """
+ Manages the entire circle packing simulation process.
+ It encapsulates hyperparameters, state, and orchestrates the multi-phase optimization.
+ """
+ def __init__(self, n=26):
+ self.n = n
+ # Hyperparameters
+ self.main_iterations = 1000
+ self.main_learning_rate = 0.01
+ self.initial_growth_pressure = 1.015
+ self.final_growth_pressure = 1.0005
+
+ self.finetune_iterations = 250
+ self.finetune_learning_rate = 0.001
+
+ self.wall_repulsion_strength = 0.25
+
+ # State
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_circle_pos):
+ """Creates the initial 5x5 grid and places the 26th circle."""
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_circle_pos
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """Calculates repulsion forces using efficient vectorized operations."""
+ forces = np.zeros_like(centers)
+ inflated_radii = radii * growth_pressure_factor
+
+ # Vectorized circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ wall_forces = np.zeros_like(centers)
+ wall_forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 0])
+ wall_forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 1])
+ wall_forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 1] + inflated_radii) - 1.0)
+ forces += wall_forces
+
+ return forces
+
+ def _run_simulation_phase(self, centers, iterations, lr, initial_pressure, final_pressure):
+ """Runs a single phase of the simulation with annealed parameters."""
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers)
+
+ # Anneal growth pressure (linear decay)
+ pressure_factor = initial_pressure - (initial_pressure - final_pressure) * (sim_iter / iterations)
+
+ forces = self._calculate_forces(centers, radii, pressure_factor)
+
+ # Anneal learning rate (quadratic decay)
+ current_lr = lr * (1.0 - (sim_iter / iterations))**2
+
+ centers += forces * current_lr
+ centers = np.clip(centers, 0.0, 1.0)
+ return centers
+
+ def solve(self):
+ """Orchestrates the full optimization process and returns the best result."""
+ # Multi-start strategy: test different initial positions for the 26th circle.
+ candidate_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial
+ ]
+
+ for initial_pos in candidate_positions:
+ # --- Main Exploration Phase ---
+ initial_centers = self._initialize_centers(initial_pos)
+ main_phase_centers = self._run_simulation_phase(
+ initial_centers, self.main_iterations, self.main_learning_rate,
+ self.initial_growth_pressure, self.final_growth_pressure
+ )
+
+ # --- Fine-Tuning Phase ---
+ # This phase uses a small learning rate and no growth pressure for precision.
+ final_centers_trial = self._run_simulation_phase(
+ main_phase_centers, self.finetune_iterations, self.finetune_learning_rate,
+ 1.0, 1.0 # No growth pressure
+ )
+
+ # Check if this trial yielded a better result
+ current_sum_radii = np.sum(compute_max_radii(final_centers_trial))
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = final_centers_trial.copy()
+
+ final_radii = compute_max_radii(self.best_final_centers)
+ return self.best_final_centers, final_radii
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by instantiating and running the SimulationManager.
+ This function acts as a clean entry point to the object-oriented simulation logic.
+ """
+ manager = SimulationManager(n=26)
+ centers, radii = manager.solve()
+ return centers, radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ca7b6630c12d61aeb857f5926cfd4336f8220899
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..9fbd683d8f5d247be212471cae415570150e8820
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/edit.diff
@@ -0,0 +1,236 @@
+--- a/original.py
++++ b/original.py
+@@ -1,199 +1,216 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ This version is a crossover, combining the high-level strategy of the best-performing
+ parent (multi-start, two-phase simulation, annealed growth pressure) with the
+ efficient vectorized force calculations from the inspiration program.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- # Increased iterations due to vectorized efficiency
+- iterations = 750
++ # Increased iterations for more thorough exploration
++ iterations = 1200
+ learning_rate = 0.01
+- wall_repulsion_strength = 0.5
+- initial_growth_pressure = 1.01
+- final_growth_pressure = 1.001
++ wall_repulsion_strength = 0.35 # Slightly softer walls than 0.5, but firmer than 0.25 in G37 for better boundary utilization
++ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
++ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
++
++ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
++ perturbation_scale = 0.002 # A small fraction of the spacing
++ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
++
+ current_centers[25] = initial_pos_26th_circle
++
++ # Ensure all centers stay within bounds after initial setup and perturbation
++ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 100 # Increased due to efficiency
+- fine_tune_learning_rate = 0.001
++ fine_tune_iterations = 200 # Increased iterations for more precise settling
++ fine_tune_learning_rate = 0.0005 # Even smaller LR for very fine adjustments
++ fine_tune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
++ fine_tune_growth_pressure_end = 1.00001 # Anneal it down to almost none for final precision
+ centers_for_sim = best_final_centers.copy()
+
+- for _ in range(fine_tune_iterations):
++ for fine_tune_iter in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+- # --- Vectorized Force Calculation (no growth pressure) ---
++ # Anneal subtle growth pressure (similar to main phase, but milder)
++ current_fine_tune_growth_pressure = fine_tune_growth_pressure_start - \
++ (fine_tune_growth_pressure_start - fine_tune_growth_pressure_end) * \
++ (fine_tune_iter / fine_tune_iterations)**2 # Quadratic annealing for growth pressure
++ inflated_radii = radii * current_fine_tune_growth_pressure # Use inflated radii for forces
++
++ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_radii_pairs - dists)
++ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
++
++ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+- overlap_bottom = radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
++ overlap_left = inflated_radii - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
++ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+- centers_for_sim += forces * fine_tune_learning_rate
++ current_lr_fine_tune = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing for fine-tune LR
++ centers_for_sim += forces * current_lr_fine_tune
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0a61ab6c7ce7a95da3976cc0ac2e9d4062b84b8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/main.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations for more thorough exploration
+ iterations = 1200
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.35 # Slightly softer walls than 0.5, but firmer than 0.25 in G37 for better boundary utilization
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 200 # Increased iterations for more precise settling
+ fine_tune_learning_rate = 0.0005 # Even smaller LR for very fine adjustments
+ fine_tune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ fine_tune_growth_pressure_end = 1.00001 # Anneal it down to almost none for final precision
+ centers_for_sim = best_final_centers.copy()
+
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (similar to main phase, but milder)
+ current_fine_tune_growth_pressure = fine_tune_growth_pressure_start - \
+ (fine_tune_growth_pressure_start - fine_tune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)**2 # Quadratic annealing for growth pressure
+ inflated_radii = radii * current_fine_tune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_fine_tune = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing for fine-tune LR
+ centers_for_sim += forces * current_lr_fine_tune
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d8b70cd02cf01ce1f4cb7c08d8829ad74d35c7a2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/original.py
@@ -0,0 +1,199 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..aec5693ab59f27e552ae54fafae3eb597f54f48a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results
+Run 1/1 completed in 25.85 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.938923372649839
+ public: {'centers_str': ' centers[0] = (0.1006, 0.0997)\n centers[1] = (0.0999, 0.3001)\n centers[2] = (0.1004, 0.5003)\n centers[3] = (0.1002, 0.7003)\n centers[4] = (0.1010, 0.8994)\n centers[5] = (0.3056, 0.0945)\n centers[6] = (0.3004, 0.3001)\n centers[7] = (0.3009, 0.5001)\n centers[8] = (0.3001, 0.6999)\n centers[9] = (0.3003, 0.9001)\n centers[10] = (0.5002, 0.0991)\n centers[11] = (0.5000, 0.3003)\n centers[12] = (0.5006, 0.5002)\n centers[13] = (0.4997, 0.7003)\n centers[14] = (0.4997, 0.8996)\n centers[15] = (0.6994, 0.0995)\n centers[16] = (0.7002, 0.3002)\n centers[17] = (0.7003, 0.4921)\n centers[18] = (0.6930, 0.6992)\n centers[19] = (0.6998, 0.9076)\n centers[20] = (0.9003, 0.0974)\n centers[21] = (0.8980, 0.2981)\n centers[22] = (0.8985, 0.5029)\n centers[23] = (0.9048, 0.7011)\n centers[24] = (0.8987, 0.8990)\n centers[25] = (0.1944, 0.2056)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.938923372649839}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/packing_viz.png
+ execution_time_mean: 25.852507180999964
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..8fc935dd806c7a9404b45f9649686e5132940fb0
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.938923372649839,
+ "public": {
+ "centers_str": " centers[0] = (0.1006, 0.0997)\n centers[1] = (0.0999, 0.3001)\n centers[2] = (0.1004, 0.5003)\n centers[3] = (0.1002, 0.7003)\n centers[4] = (0.1010, 0.8994)\n centers[5] = (0.3056, 0.0945)\n centers[6] = (0.3004, 0.3001)\n centers[7] = (0.3009, 0.5001)\n centers[8] = (0.3001, 0.6999)\n centers[9] = (0.3003, 0.9001)\n centers[10] = (0.5002, 0.0991)\n centers[11] = (0.5000, 0.3003)\n centers[12] = (0.5006, 0.5002)\n centers[13] = (0.4997, 0.7003)\n centers[14] = (0.4997, 0.8996)\n centers[15] = (0.6994, 0.0995)\n centers[16] = (0.7002, 0.3002)\n centers[17] = (0.7003, 0.4921)\n centers[18] = (0.6930, 0.6992)\n centers[19] = (0.6998, 0.9076)\n centers[20] = (0.9003, 0.0974)\n centers[21] = (0.8980, 0.2981)\n centers[22] = (0.8985, 0.5029)\n centers[23] = (0.9048, 0.7011)\n centers[24] = (0.8987, 0.8990)\n centers[25] = (0.1944, 0.2056)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.938923372649839
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/results/packing_viz.png",
+ "execution_time_mean": 25.852507180999964,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9b8a1b2b2a37d337f0b83c9c2a31a56b6c4d3521
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_43/search_replace.txt
@@ -0,0 +1,148 @@
+
+Adjust main simulation hyperparameters for more thorough exploration.
+
+
+
+<<<<<<< SEARCH
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+=======
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations for more thorough exploration
+ iterations = 1200
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.35 # Slightly softer walls than 0.5, but firmer than 0.25 in G37 for better boundary utilization
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+>>>>>>> REPLACE
+
+
+
+Add small random perturbations to the initial 5x5 grid (first 25 circles) to break symmetry and aid in escaping local minima. The perturbation is applied before the 26th circle is placed and the array is clipped to bounds.
+
+
+
+<<<<<<< SEARCH
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+=======
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+>>>>>>> REPLACE
+
+
+
+Reintroduce a subtle, annealed growth pressure in the fine-tuning phase to encourage precise, last-stage expansion and tighter packing. Also, increase fine-tuning iterations and reduce learning rate for higher precision.
+
+
+
+<<<<<<< SEARCH
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+=======
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 200 # Increased iterations for more precise settling
+ fine_tune_learning_rate = 0.0005 # Even smaller LR for very fine adjustments
+ fine_tune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ fine_tune_growth_pressure_end = 1.00001 # Anneal it down to almost none for final precision
+ centers_for_sim = best_final_centers.copy()
+
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (similar to main phase, but milder)
+ current_fine_tune_growth_pressure = fine_tune_growth_pressure_start - \
+ (fine_tune_growth_pressure_start - fine_tune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)**2 # Quadratic annealing for growth pressure
+ inflated_radii = radii * current_fine_tune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_fine_tune = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing for fine-tune LR
+ centers_for_sim += forces * current_lr_fine_tune
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..4d84a8b8ac5c047dbefe0b85f1e348659922da57
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/edit.diff
@@ -0,0 +1,232 @@
+--- a/original.py
++++ b/original.py
+@@ -1,199 +1,210 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm is a crossover, combining the high-level strategy of the best-performing
+ parent (multi-start, two-phase simulation, annealed growth pressure) with the
+ efficient vectorized force calculations from the inspiration program.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- # Increased iterations due to vectorized efficiency
+- iterations = 750
++ # Tuned hyperparameters based on successful class-based implementation (G37 equivalent)
++ iterations = 1200 # Increased for more thorough exploration
+ learning_rate = 0.01
+- wall_repulsion_strength = 0.5
+- initial_growth_pressure = 1.01
+- final_growth_pressure = 1.001
++ wall_repulsion_strength = 0.3 # Slightly adjusted wall repulsion
++ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
++ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+- # Anneal growth pressure
+- current_growth_pressure = initial_growth_pressure - \
+- (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
++ # Anneal growth pressure quadratically
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * \
++ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 100 # Increased due to efficiency
+- fine_tune_learning_rate = 0.001
++ fine_tune_iterations = 300 # Increased fine-tune iterations for precision
++ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
++ finetune_growth_pressure_start = 1.0005 # Small growth pressure in fine-tune
++ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
+ centers_for_sim = best_final_centers.copy()
+
+- for _ in range(fine_tune_iterations):
++ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+- # --- Vectorized Force Calculation (no growth pressure) ---
++ # Anneal growth pressure for fine-tuning (linear decay for stability)
++ current_finetune_growth_pressure = finetune_growth_pressure_start - \
++ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
++ (sim_iter_ft / fine_tune_iterations)
++ inflated_radii_ft = radii * current_finetune_growth_pressure
++
++ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
++ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+- # b. Wall forces
++ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+- overlap_bottom = radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
++ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
++ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+- centers_for_sim += forces * fine_tune_learning_rate
++ # Anneal learning rate for fine-tuning (linear decay)
++ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
++ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..d4d68d3cdc63b54772c63b3327f0a823d19d77c3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/main.py
@@ -0,0 +1,210 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters based on successful class-based implementation (G37 equivalent)
+ iterations = 1200 # Increased for more thorough exploration
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.3 # Slightly adjusted wall repulsion
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 300 # Increased fine-tune iterations for precision
+ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
+ finetune_growth_pressure_start = 1.0005 # Small growth pressure in fine-tune
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure for fine-tuning (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..41788ebfff77a41763a8bd844dbf24483d23d2e7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/original.py
@@ -0,0 +1,199 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d104d4d4d1fd19f665ca6a465f5d14b07bb3bcaf
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_44/search_replace.txt
@@ -0,0 +1,290 @@
+
+tuned_hyperparameters_and_annealing_monolithic
+
+
+
+This edit integrates several successful hyperparameter tunings and annealing strategies from the class-based version into this monolithic function structure.
+
+Key changes:
+1. **Main Phase Iterations and Learning Rate:** `iterations` increased from 750 to 1200 for more extensive exploration. The `learning_rate` remains at 0.01 but its quadratic annealing schedule is retained.
+2. **Main Phase Growth Pressure:** `initial_growth_pressure` is increased to 1.018 and `final_growth_pressure` reduced to 1.0001, combined with a quadratic annealing schedule. This provides stronger initial repulsion and more precise settling towards the end of the main phase.
+3. **Wall Repulsion Strength:** `wall_repulsion_strength` is reduced from 0.5 to 0.3, allowing circles to pack more tightly against the boundaries.
+4. **Fine-Tuning Iterations and Learning Rate:** `fine_tune_iterations` is increased from 100 to 300 for greater precision. `fine_tune_learning_rate` is reduced to 0.0008, and a linear annealing schedule is introduced for it.
+5. **Fine-Tuning Growth Pressure:** A subtle growth pressure is introduced for the fine-tuning phase (`finetune_growth_pressure_start=1.0005`, `finetune_growth_pressure_end=1.00001`), with linear annealing. This helps resolve very small overlaps and encourages a slightly denser packing in the final stages.
+6. **Consistency:** Force calculations in the fine-tuning phase now consistently use the `inflated_radii_ft` when growth pressure is active.
+
+
+
+<<<<<<< SEARCH
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+=======
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters based on successful class-based implementation (G37 equivalent)
+ iterations = 1200 # Increased for more thorough exploration
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.3 # Slightly adjusted wall repulsion
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 300 # Increased fine-tune iterations for precision
+ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
+ finetune_growth_pressure_start = 1.0005 # Small growth pressure in fine-tune
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure for fine-tuning (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9a33083d1184c06a31fe087cd066bccf90b7227b
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..85406864ea568f6e787bc76505a96ec62d9a2bff
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/edit.diff
@@ -0,0 +1,256 @@
+--- a/original.py
++++ b/original.py
+@@ -1,215 +1,223 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Refine the strong initial grid using a force-directed simulation. This allows
+ # the circles to shift from the rigid grid into a more optimal packing.
+ centers = refine_centers_with_simulation(centers)
+
+ # Compute maximum valid radii for the refined configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+ def refine_centers_with_simulation(centers):
+ """
+ Refines circle centers using a two-phase force-directed simulation inspired
+ by high-performance prior implementations. It first uses an annealed 'growth
+ pressure' to explore the solution space, then fine-tunes the positions.
+ """
+ n = centers.shape[0]
+
+- # --- Hyperparameters from best-performing prior program ---
+- sim_iter = 1000
++ # --- Hyperparameters from best-performing prior program, tuned for longer simulation ---
++ sim_iter = 2000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+- fine_tune_iter = 200
++ fine_tune_iter = 400
+ fine_tune_lr = 0.001
++ radius_sim_iter = 75 # Drastically reduced from 500 for performance
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+- # 1. Calculate radii for current positions
+- current_radii = compute_max_radii(centers)
++ # 1. Calculate approximate radii for current positions
++ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b) Wall repulsion
+- for i in range(n):
+- overlap_l = pressured_radii[i] - centers[i, 0]
+- if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+- overlap_r = (centers[i, 0] + pressured_radii[i]) - 1.0
+- if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+- overlap_b = pressured_radii[i] - centers[i, 1]
+- if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+- overlap_t = (centers[i, 1] + pressured_radii[i]) - 1.0
+- if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
++ # b) Vectorized Wall repulsion
++ # Left wall (x=0)
++ overlap_l = pressured_radii - centers[:, 0]
++ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
++ # Right wall (x=1)
++ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
++ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
++ # Bottom wall (y=0)
++ overlap_b = pressured_radii - centers[:, 1]
++ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
++ # Top wall (y=1)
++ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
++ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+- # 1. Calculate radii based on actual positions
+- current_radii = compute_max_radii(centers)
++ # 1. Calculate approximate radii based on actual positions
++ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b) Wall repulsion (based on actual radii)
+- for i in range(n):
+- overlap_l = current_radii[i] - centers[i, 0]
+- if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+- overlap_r = (centers[i, 0] + current_radii[i]) - 1.0
+- if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+- overlap_b = current_radii[i] - centers[i, 1]
+- if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+- overlap_t = (centers[i, 1] + current_radii[i]) - 1.0
+- if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
++ # b) Vectorized Wall repulsion (based on actual radii)
++ # Left wall (x=0)
++ overlap_l = current_radii - centers[:, 0]
++ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
++ # Right wall (x=1)
++ overlap_r = (centers[:, 0] + current_radii) - 1.0
++ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
++ # Bottom wall (y=0)
++ overlap_b = current_radii - centers[:, 1]
++ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
++ # Top wall (y=1)
++ overlap_t = (centers[:, 1] + current_radii) - 1.0
++ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+-def compute_max_radii(centers):
++def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
++ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+- # Increased iterations to ensure full convergence for a dense packing.
+- for _ in range(500): # Iterate more times to stabilize radii
++ # The number of iterations is parameterized for performance tuning.
++ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f3e6f99d2f72d27c3d8749c3f4c93249a85af6e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/main.py
@@ -0,0 +1,223 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Refine the strong initial grid using a force-directed simulation. This allows
+ # the circles to shift from the rigid grid into a more optimal packing.
+ centers = refine_centers_with_simulation(centers)
+
+ # Compute maximum valid radii for the refined configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def refine_centers_with_simulation(centers):
+ """
+ Refines circle centers using a two-phase force-directed simulation inspired
+ by high-performance prior implementations. It first uses an annealed 'growth
+ pressure' to explore the solution space, then fine-tunes the positions.
+ """
+ n = centers.shape[0]
+
+ # --- Hyperparameters from best-performing prior program, tuned for longer simulation ---
+ sim_iter = 2000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 400
+ fine_tune_lr = 0.001
+ radius_sim_iter = 75 # Drastically reduced from 500 for performance
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # The number of iterations is parameterized for performance tuning.
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..e5fee9675192b2a1a8fa9f1ebbf1408780c38676
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/original.py
@@ -0,0 +1,215 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Refine the strong initial grid using a force-directed simulation. This allows
+ # the circles to shift from the rigid grid into a more optimal packing.
+ centers = refine_centers_with_simulation(centers)
+
+ # Compute maximum valid radii for the refined configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def refine_centers_with_simulation(centers):
+ """
+ Refines circle centers using a two-phase force-directed simulation inspired
+ by high-performance prior implementations. It first uses an annealed 'growth
+ pressure' to explore the solution space, then fine-tunes the positions.
+ """
+ n = centers.shape[0]
+
+ # --- Hyperparameters from best-performing prior program ---
+ sim_iter = 1000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 200
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for current positions
+ current_radii = compute_max_radii(centers)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion
+ for i in range(n):
+ overlap_l = pressured_radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+ overlap_r = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+ overlap_b = pressured_radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+ overlap_t = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii based on actual positions
+ current_radii = compute_max_radii(centers)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(n):
+ overlap_l = current_radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+ overlap_r = (centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+ overlap_b = current_radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+ overlap_t = (centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # Increased iterations to ensure full convergence for a dense packing.
+ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..ce8e483deb0a0308e750bec5369ef0580e567d94
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results
+Run 1/1 completed in 6.80 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9558479358284226
+ public: {'centers_str': ' centers[0] = (0.0945, 0.0950)\n centers[1] = (0.3103, 0.0897)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8988, 0.1012)\n centers[5] = (0.0855, 0.3145)\n centers[6] = (0.3049, 0.3053)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.6999, 0.4937)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.2933, 0.6969)\n centers[17] = (0.4737, 0.6931)\n centers[18] = (0.7039, 0.6847)\n centers[19] = (0.9174, 0.7009)\n centers[20] = (0.0912, 0.9000)\n centers[21] = (0.2932, 0.8893)\n centers[22] = (0.5096, 0.8947)\n centers[23] = (0.7070, 0.9081)\n centers[24] = (0.8995, 0.8982)\n centers[25] = (0.2048, 0.1954)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9558479358284226}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/packing_viz.png
+ execution_time_mean: 6.795371904969215
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..f638709981e3853a2a4dcda07eb55589a144dee3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9558479358284226,
+ "public": {
+ "centers_str": " centers[0] = (0.0945, 0.0950)\n centers[1] = (0.3103, 0.0897)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8988, 0.1012)\n centers[5] = (0.0855, 0.3145)\n centers[6] = (0.3049, 0.3053)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.6999, 0.4937)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.2933, 0.6969)\n centers[17] = (0.4737, 0.6931)\n centers[18] = (0.7039, 0.6847)\n centers[19] = (0.9174, 0.7009)\n centers[20] = (0.0912, 0.9000)\n centers[21] = (0.2932, 0.8893)\n centers[22] = (0.5096, 0.8947)\n centers[23] = (0.7070, 0.9081)\n centers[24] = (0.8995, 0.8982)\n centers[25] = (0.2048, 0.1954)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9558479358284226
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/results/packing_viz.png",
+ "execution_time_mean": 6.795371904969215,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..899e6a3eaa28cfd9252b3d441464dc1d57397a55
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_45/search_replace.txt
@@ -0,0 +1,246 @@
+Excellent, I will propose a set of synergistic improvements to enhance the performance and quality of the circle packing algorithm. The core idea is to significantly boost the simulation's computational efficiency, and then reinvest that newfound speed into a more extensive search for a better packing configuration.
+
+My analysis of the current program and its performance reveals two key bottlenecks:
+1. **Expensive Radius Calculation:** The `compute_max_radii` function with 500 iterations is called within every single simulation step, which is computationally excessive for generating the approximate radii needed to guide the force simulation.
+2. **Inefficient Wall Repulsion:** The wall repulsion forces are calculated using a Python `for` loop, which is much slower than vectorized NumPy operations.
+
+I will address these with three changes:
+1. Parameterize the number of iterations in the radius calculation function.
+2. Vectorize the wall repulsion calculation for a significant speedup.
+3. Use the performance gains from the first two changes to double the duration of both the main refinement and the final fine-tuning simulations, giving the system more time to settle into a higher-quality packing.
+
+
+parameterize_radius_iterations
+
+
+The `compute_max_radii` function is called within every step of the simulation loops with a hardcoded 500 iterations. This is computationally expensive and unnecessary for the intermediate steps where an approximation is sufficient.
+
+This edit introduces an `iterations` parameter to `compute_max_radii`, with a default of 500 to maintain accuracy for the final calculation. This refactoring allows us to call the function with fewer iterations inside the simulation loops, paving the way for significant performance improvements.
+
+
+<<<<<<< SEARCH
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # Increased iterations to ensure full convergence for a dense packing.
+ for _ in range(500): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+=======
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # The number of iterations is parameterized for performance tuning.
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+>>>>>>> REPLACE
+
+
+vectorize_wall_repulsion
+
+
+The wall repulsion forces were previously calculated inside an inefficient Python `for` loop. This change replaces the loop with vectorized NumPy operations for both the main simulation and the fine-tuning phase. By calculating overlaps with all four walls for all circles simultaneously, we eliminate the Python loop overhead. This provides a significant performance boost, allowing the simulation to run faster and ultimately achieve better converged packings.
+
+
+<<<<<<< SEARCH
+ # b) Wall repulsion
+ for i in range(n):
+ overlap_l = pressured_radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+ overlap_r = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+ overlap_b = pressured_radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+ overlap_t = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+=======
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ # b) Wall repulsion (based on actual radii)
+ for i in range(n):
+ overlap_l = current_radii[i] - centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+ overlap_r = (centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+ overlap_b = current_radii[i] - centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+ overlap_t = (centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+=======
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+>>>>>>> REPLACE
+
+
+increase_simulation_duration
+
+
+To capitalize on the performance gains from the previous changes, this edit reinvests the saved computation time into a more thorough simulation. The number of radius iterations inside the simulation loops is drastically reduced to 75. In turn, the number of main simulation iterations is doubled to 2000, and fine-tuning iterations are doubled to 400. This provides the force-directed algorithm substantially more time to explore the solution space and settle into a more optimal, higher-density packing, increasing the chances of breaking away from the initial rigid grid structure.
+
+
+<<<<<<< SEARCH
+ # --- Hyperparameters from best-performing prior program ---
+ sim_iter = 1000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 200
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for current positions
+ current_radii = compute_max_radii(centers)
+=======
+ # --- Hyperparameters from best-performing prior program, tuned for longer simulation ---
+ sim_iter = 2000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 400
+ fine_tune_lr = 0.001
+ radius_sim_iter = 75 # Drastically reduced from 500 for performance
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii based on actual positions
+ current_radii = compute_max_radii(centers)
+=======
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..61d7f696bbda6d3da3834433bd4dacc862b3892c
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..be9ea8286d33ca55335961f91e22f4d1afdc0685
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/edit.diff
@@ -0,0 +1,270 @@
+--- a/original.py
++++ b/original.py
+@@ -1,257 +1,257 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ This version refactors the simulation into a modular class-based structure
+ to improve organization, maintainability, and allow for more advanced
+ annealing schedules and initialization strategies.
+ """
+
+ import numpy as np
+
+ # Helper function (remains outside the class as it's stateless)
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for stability based on prior insights
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Robust handling for very close centers
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+ class CirclePackingOptimizer:
+ """
+ Encapsulates the logic for optimizing circle packing using a physical simulation approach.
+ Manages hyperparameters, initialization, force calculations, and simulation phases.
+ """
+ def __init__(self, n=26,
+ iterations_main=1000, learning_rate_main=0.01,
+ initial_growth_pressure=1.015, final_growth_pressure=1.0005,
+ wall_repulsion_strength=0.25,
+ fine_tune_iterations=250, fine_tune_learning_rate=0.001,
+ fine_tune_growth_pressure_start=1.0, fine_tune_growth_pressure_end=1.0,
+ initial_perturbation_scale=0.0):
+
+ self.n = n
+ self.iterations_main = iterations_main
+ self.learning_rate_main = learning_rate_main
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.wall_repulsion_strength = wall_repulsion_strength
+ self.fine_tune_iterations = fine_tune_iterations
+ self.fine_tune_learning_rate = fine_tune_learning_rate
+ self.fine_tune_growth_pressure_start = fine_tune_growth_pressure_start
+ self.fine_tune_growth_pressure_end = fine_tune_growth_pressure_end
+ self.initial_perturbation_scale = initial_perturbation_scale # Recommendation 4
+
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_pos_26th_circle):
+ """
+ Initializes centers with a 5x5 grid and places the 26th circle.
+ Applies a small random perturbation if `self.initial_perturbation_scale` is > 0.
+ (Implementation for Recommendation 4)
+ """
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_pos_26th_circle
+
+ if self.initial_perturbation_scale > 0:
+ centers += np.random.normal(0, spacing * self.initial_perturbation_scale, size=centers.shape)
+ centers = np.clip(centers, 0.0, 1.0) # Ensure centers stay within bounds after perturbation
+
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """
+ Calculates total forces (circle-to-circle and wall repulsion) for a given state.
+ Uses vectorized operations for efficiency.
+ """
+ forces = np.zeros_like(centers)
+
+ # Apply growth pressure to radii
+ inflated_radii = radii * growth_pressure_factor
+
+ # a. Circle-to-circle repulsion forces
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ # Calculate overlaps, ensuring no self-overlap
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ # Calculate unit vectors for directions
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0 # Avoid division by zero for identical centers
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+ forces += circle_forces
+
+ # b. Wall repulsion forces
+ overlap_left = inflated_radii - centers[:, 0]
+ overlap_right = (centers[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers[:, 1]
+ overlap_top = (centers[:, 1] + inflated_radii) - 1.0
+
+ forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, overlap_left)
+ forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, overlap_right)
+ forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ return forces
+
+ def _run_simulation_phase(self, initial_centers, iterations, learning_rate_init,
+ growth_pressure_anneal_start=1.0, growth_pressure_anneal_end=1.0,
+ learning_rate_annealing_exponent=2.0):
+ """
+ Runs a single simulation phase with specified parameters, applying annealing schedules.
+ (Implements Recommendation 2 for growth pressure annealing)
+ """
+ centers_for_sim = initial_centers.copy()
+
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure (quadratic schedule, Recommendation 2)
+ growth_pressure_factor = growth_pressure_anneal_end + \
+ (growth_pressure_anneal_start - growth_pressure_anneal_end) * \
+ (1.0 - (sim_iter / iterations))**2
+
+ forces = self._calculate_forces(centers_for_sim, radii, growth_pressure_factor)
+
+ # Anneal learning rate (quadratic schedule for main, linear for fine-tune)
+ current_lr = learning_rate_init * (1.0 - (sim_iter / iterations))**learning_rate_annealing_exponent
+
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ return centers_for_sim
+
+ def solve(self):
+ """
+ Orchestrates the multi-start simulation and fine-tuning phases.
+ """
+ # Candidate initial positions for the 26th circle (Recommendation 1 implicitly handled by multi-start)
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Main Simulation Phase ---
+ # Initialize centers for the current trial, with optional small perturbation
+ initial_centers_trial = self._initialize_centers(initial_pos_26th_circle)
+
+ simulated_centers = self._run_simulation_phase(
+ initial_centers_trial,
+ self.iterations_main,
+ self.learning_rate_main,
+ self.initial_growth_pressure,
+ self.final_growth_pressure,
+ learning_rate_annealing_exponent=2.0 # Quadratic LR annealing for main phase
+ )
+
+ current_sum_radii = np.sum(compute_max_radii(simulated_centers))
+
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = simulated_centers.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Start fine-tuning from the best configuration found in the main phase.
+ if self.best_final_centers is not None:
+ fine_tuned_centers = self._run_simulation_phase(
+ self.best_final_centers,
+ self.fine_tune_iterations,
+ self.fine_tune_learning_rate,
+ self.fine_tune_growth_pressure_start, # Start with a subtle growth pressure (Recommendation 3)
+ self.fine_tune_growth_pressure_end, # Anneal it down for precise packing (Recommendation 3)
+ learning_rate_annealing_exponent=1.0 # Linear LR annealing for fine-tuning
+ )
+ final_centers = fine_tuned_centers
+ else: # Fallback if no valid centers were found (should not happen with good candidates)
+ final_centers = self._initialize_centers([0.5, 0.5]) # Default to center if nothing better found
+
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Instantiate the optimizer with desired hyperparameters.
+ # These parameters are tuned based on observations and recommendations.
+ optimizer = CirclePackingOptimizer(
+ n=26,
+- iterations_main=1200, # Increased for more thorough exploration
+- learning_rate_main=0.01,
+- initial_growth_pressure=1.018, # Slightly higher initial pressure for stronger repulsion
+- final_growth_pressure=1.0001, # Closer to 1.0 for precise packing at end of main phase
+- wall_repulsion_strength=0.3, # Slightly adjusted wall repulsion
+- fine_tune_iterations=300, # Increased fine-tune iterations for precision
+- fine_tune_learning_rate=0.0008, # Slower fine-tuning for high precision
+- fine_tune_growth_pressure_start=1.0005, # Small growth pressure in fine-tune (Recommendation 3)
+- fine_tune_growth_pressure_end=1.00001, # Almost zero growth pressure at the end of fine-tune (Recommendation 3)
+- initial_perturbation_scale=0.005 # Small random perturbation to initial grid (Recommendation 4)
++ iterations_main=1500, # More iterations to settle after explosion
++ learning_rate_main=0.02, # Higher LR for bigger steps
++ initial_growth_pressure=1.20, # Drastically higher initial pressure for "explosion"
++ final_growth_pressure=1.001, # End pressure can be slightly higher
++ wall_repulsion_strength=0.3, # Keep this, seems reasonable
++ fine_tune_iterations=400, # More fine-tuning needed after a chaotic main phase
++ fine_tune_learning_rate=0.0005, # Slower fine-tuning for precision
++ fine_tune_growth_pressure_start=1.001, # Slightly more pressure in fine-tune start
++ fine_tune_growth_pressure_end=1.00001, # Kept for final precision
++ initial_perturbation_scale=0.01 # Slightly larger perturbation to help break symmetry
+ )
+
+ return optimizer.solve()
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..b636201e817046f98987a724c4d696578dfd9887
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/main.py
@@ -0,0 +1,257 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version refactors the simulation into a modular class-based structure
+to improve organization, maintainability, and allow for more advanced
+annealing schedules and initialization strategies.
+"""
+
+import numpy as np
+
+# Helper function (remains outside the class as it's stateless)
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for stability based on prior insights
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Robust handling for very close centers
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+class CirclePackingOptimizer:
+ """
+ Encapsulates the logic for optimizing circle packing using a physical simulation approach.
+ Manages hyperparameters, initialization, force calculations, and simulation phases.
+ """
+ def __init__(self, n=26,
+ iterations_main=1000, learning_rate_main=0.01,
+ initial_growth_pressure=1.015, final_growth_pressure=1.0005,
+ wall_repulsion_strength=0.25,
+ fine_tune_iterations=250, fine_tune_learning_rate=0.001,
+ fine_tune_growth_pressure_start=1.0, fine_tune_growth_pressure_end=1.0,
+ initial_perturbation_scale=0.0):
+
+ self.n = n
+ self.iterations_main = iterations_main
+ self.learning_rate_main = learning_rate_main
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.wall_repulsion_strength = wall_repulsion_strength
+ self.fine_tune_iterations = fine_tune_iterations
+ self.fine_tune_learning_rate = fine_tune_learning_rate
+ self.fine_tune_growth_pressure_start = fine_tune_growth_pressure_start
+ self.fine_tune_growth_pressure_end = fine_tune_growth_pressure_end
+ self.initial_perturbation_scale = initial_perturbation_scale # Recommendation 4
+
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_pos_26th_circle):
+ """
+ Initializes centers with a 5x5 grid and places the 26th circle.
+ Applies a small random perturbation if `self.initial_perturbation_scale` is > 0.
+ (Implementation for Recommendation 4)
+ """
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_pos_26th_circle
+
+ if self.initial_perturbation_scale > 0:
+ centers += np.random.normal(0, spacing * self.initial_perturbation_scale, size=centers.shape)
+ centers = np.clip(centers, 0.0, 1.0) # Ensure centers stay within bounds after perturbation
+
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """
+ Calculates total forces (circle-to-circle and wall repulsion) for a given state.
+ Uses vectorized operations for efficiency.
+ """
+ forces = np.zeros_like(centers)
+
+ # Apply growth pressure to radii
+ inflated_radii = radii * growth_pressure_factor
+
+ # a. Circle-to-circle repulsion forces
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ # Calculate overlaps, ensuring no self-overlap
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ # Calculate unit vectors for directions
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0 # Avoid division by zero for identical centers
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+ forces += circle_forces
+
+ # b. Wall repulsion forces
+ overlap_left = inflated_radii - centers[:, 0]
+ overlap_right = (centers[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers[:, 1]
+ overlap_top = (centers[:, 1] + inflated_radii) - 1.0
+
+ forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, overlap_left)
+ forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, overlap_right)
+ forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ return forces
+
+ def _run_simulation_phase(self, initial_centers, iterations, learning_rate_init,
+ growth_pressure_anneal_start=1.0, growth_pressure_anneal_end=1.0,
+ learning_rate_annealing_exponent=2.0):
+ """
+ Runs a single simulation phase with specified parameters, applying annealing schedules.
+ (Implements Recommendation 2 for growth pressure annealing)
+ """
+ centers_for_sim = initial_centers.copy()
+
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure (quadratic schedule, Recommendation 2)
+ growth_pressure_factor = growth_pressure_anneal_end + \
+ (growth_pressure_anneal_start - growth_pressure_anneal_end) * \
+ (1.0 - (sim_iter / iterations))**2
+
+ forces = self._calculate_forces(centers_for_sim, radii, growth_pressure_factor)
+
+ # Anneal learning rate (quadratic schedule for main, linear for fine-tune)
+ current_lr = learning_rate_init * (1.0 - (sim_iter / iterations))**learning_rate_annealing_exponent
+
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ return centers_for_sim
+
+ def solve(self):
+ """
+ Orchestrates the multi-start simulation and fine-tuning phases.
+ """
+ # Candidate initial positions for the 26th circle (Recommendation 1 implicitly handled by multi-start)
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Main Simulation Phase ---
+ # Initialize centers for the current trial, with optional small perturbation
+ initial_centers_trial = self._initialize_centers(initial_pos_26th_circle)
+
+ simulated_centers = self._run_simulation_phase(
+ initial_centers_trial,
+ self.iterations_main,
+ self.learning_rate_main,
+ self.initial_growth_pressure,
+ self.final_growth_pressure,
+ learning_rate_annealing_exponent=2.0 # Quadratic LR annealing for main phase
+ )
+
+ current_sum_radii = np.sum(compute_max_radii(simulated_centers))
+
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = simulated_centers.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Start fine-tuning from the best configuration found in the main phase.
+ if self.best_final_centers is not None:
+ fine_tuned_centers = self._run_simulation_phase(
+ self.best_final_centers,
+ self.fine_tune_iterations,
+ self.fine_tune_learning_rate,
+ self.fine_tune_growth_pressure_start, # Start with a subtle growth pressure (Recommendation 3)
+ self.fine_tune_growth_pressure_end, # Anneal it down for precise packing (Recommendation 3)
+ learning_rate_annealing_exponent=1.0 # Linear LR annealing for fine-tuning
+ )
+ final_centers = fine_tuned_centers
+ else: # Fallback if no valid centers were found (should not happen with good candidates)
+ final_centers = self._initialize_centers([0.5, 0.5]) # Default to center if nothing better found
+
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Instantiate the optimizer with desired hyperparameters.
+ # These parameters are tuned based on observations and recommendations.
+ optimizer = CirclePackingOptimizer(
+ n=26,
+ iterations_main=1500, # More iterations to settle after explosion
+ learning_rate_main=0.02, # Higher LR for bigger steps
+ initial_growth_pressure=1.20, # Drastically higher initial pressure for "explosion"
+ final_growth_pressure=1.001, # End pressure can be slightly higher
+ wall_repulsion_strength=0.3, # Keep this, seems reasonable
+ fine_tune_iterations=400, # More fine-tuning needed after a chaotic main phase
+ fine_tune_learning_rate=0.0005, # Slower fine-tuning for precision
+ fine_tune_growth_pressure_start=1.001, # Slightly more pressure in fine-tune start
+ fine_tune_growth_pressure_end=1.00001, # Kept for final precision
+ initial_perturbation_scale=0.01 # Slightly larger perturbation to help break symmetry
+ )
+
+ return optimizer.solve()
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..cdb4cd6c965df3c4004131766343e37d44df7eed
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/original.py
@@ -0,0 +1,257 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version refactors the simulation into a modular class-based structure
+to improve organization, maintainability, and allow for more advanced
+annealing schedules and initialization strategies.
+"""
+
+import numpy as np
+
+# Helper function (remains outside the class as it's stateless)
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for stability based on prior insights
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Robust handling for very close centers
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+class CirclePackingOptimizer:
+ """
+ Encapsulates the logic for optimizing circle packing using a physical simulation approach.
+ Manages hyperparameters, initialization, force calculations, and simulation phases.
+ """
+ def __init__(self, n=26,
+ iterations_main=1000, learning_rate_main=0.01,
+ initial_growth_pressure=1.015, final_growth_pressure=1.0005,
+ wall_repulsion_strength=0.25,
+ fine_tune_iterations=250, fine_tune_learning_rate=0.001,
+ fine_tune_growth_pressure_start=1.0, fine_tune_growth_pressure_end=1.0,
+ initial_perturbation_scale=0.0):
+
+ self.n = n
+ self.iterations_main = iterations_main
+ self.learning_rate_main = learning_rate_main
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.wall_repulsion_strength = wall_repulsion_strength
+ self.fine_tune_iterations = fine_tune_iterations
+ self.fine_tune_learning_rate = fine_tune_learning_rate
+ self.fine_tune_growth_pressure_start = fine_tune_growth_pressure_start
+ self.fine_tune_growth_pressure_end = fine_tune_growth_pressure_end
+ self.initial_perturbation_scale = initial_perturbation_scale # Recommendation 4
+
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_pos_26th_circle):
+ """
+ Initializes centers with a 5x5 grid and places the 26th circle.
+ Applies a small random perturbation if `self.initial_perturbation_scale` is > 0.
+ (Implementation for Recommendation 4)
+ """
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_pos_26th_circle
+
+ if self.initial_perturbation_scale > 0:
+ centers += np.random.normal(0, spacing * self.initial_perturbation_scale, size=centers.shape)
+ centers = np.clip(centers, 0.0, 1.0) # Ensure centers stay within bounds after perturbation
+
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """
+ Calculates total forces (circle-to-circle and wall repulsion) for a given state.
+ Uses vectorized operations for efficiency.
+ """
+ forces = np.zeros_like(centers)
+
+ # Apply growth pressure to radii
+ inflated_radii = radii * growth_pressure_factor
+
+ # a. Circle-to-circle repulsion forces
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ # Calculate overlaps, ensuring no self-overlap
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ # Calculate unit vectors for directions
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0 # Avoid division by zero for identical centers
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+ forces += circle_forces
+
+ # b. Wall repulsion forces
+ overlap_left = inflated_radii - centers[:, 0]
+ overlap_right = (centers[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers[:, 1]
+ overlap_top = (centers[:, 1] + inflated_radii) - 1.0
+
+ forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, overlap_left)
+ forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, overlap_right)
+ forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ return forces
+
+ def _run_simulation_phase(self, initial_centers, iterations, learning_rate_init,
+ growth_pressure_anneal_start=1.0, growth_pressure_anneal_end=1.0,
+ learning_rate_annealing_exponent=2.0):
+ """
+ Runs a single simulation phase with specified parameters, applying annealing schedules.
+ (Implements Recommendation 2 for growth pressure annealing)
+ """
+ centers_for_sim = initial_centers.copy()
+
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure (quadratic schedule, Recommendation 2)
+ growth_pressure_factor = growth_pressure_anneal_end + \
+ (growth_pressure_anneal_start - growth_pressure_anneal_end) * \
+ (1.0 - (sim_iter / iterations))**2
+
+ forces = self._calculate_forces(centers_for_sim, radii, growth_pressure_factor)
+
+ # Anneal learning rate (quadratic schedule for main, linear for fine-tune)
+ current_lr = learning_rate_init * (1.0 - (sim_iter / iterations))**learning_rate_annealing_exponent
+
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ return centers_for_sim
+
+ def solve(self):
+ """
+ Orchestrates the multi-start simulation and fine-tuning phases.
+ """
+ # Candidate initial positions for the 26th circle (Recommendation 1 implicitly handled by multi-start)
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Main Simulation Phase ---
+ # Initialize centers for the current trial, with optional small perturbation
+ initial_centers_trial = self._initialize_centers(initial_pos_26th_circle)
+
+ simulated_centers = self._run_simulation_phase(
+ initial_centers_trial,
+ self.iterations_main,
+ self.learning_rate_main,
+ self.initial_growth_pressure,
+ self.final_growth_pressure,
+ learning_rate_annealing_exponent=2.0 # Quadratic LR annealing for main phase
+ )
+
+ current_sum_radii = np.sum(compute_max_radii(simulated_centers))
+
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = simulated_centers.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Start fine-tuning from the best configuration found in the main phase.
+ if self.best_final_centers is not None:
+ fine_tuned_centers = self._run_simulation_phase(
+ self.best_final_centers,
+ self.fine_tune_iterations,
+ self.fine_tune_learning_rate,
+ self.fine_tune_growth_pressure_start, # Start with a subtle growth pressure (Recommendation 3)
+ self.fine_tune_growth_pressure_end, # Anneal it down for precise packing (Recommendation 3)
+ learning_rate_annealing_exponent=1.0 # Linear LR annealing for fine-tuning
+ )
+ final_centers = fine_tuned_centers
+ else: # Fallback if no valid centers were found (should not happen with good candidates)
+ final_centers = self._initialize_centers([0.5, 0.5]) # Default to center if nothing better found
+
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Instantiate the optimizer with desired hyperparameters.
+ # These parameters are tuned based on observations and recommendations.
+ optimizer = CirclePackingOptimizer(
+ n=26,
+ iterations_main=1200, # Increased for more thorough exploration
+ learning_rate_main=0.01,
+ initial_growth_pressure=1.018, # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure=1.0001, # Closer to 1.0 for precise packing at end of main phase
+ wall_repulsion_strength=0.3, # Slightly adjusted wall repulsion
+ fine_tune_iterations=300, # Increased fine-tune iterations for precision
+ fine_tune_learning_rate=0.0008, # Slower fine-tuning for high precision
+ fine_tune_growth_pressure_start=1.0005, # Small growth pressure in fine-tune (Recommendation 3)
+ fine_tune_growth_pressure_end=1.00001, # Almost zero growth pressure at the end of fine-tune (Recommendation 3)
+ initial_perturbation_scale=0.005 # Small random perturbation to initial grid (Recommendation 4)
+ )
+
+ return optimizer.solve()
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..ffa8efccb9627e9ab46be25b7ac570d37a4ae3c1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results
+Run 1/1 completed in 31.11 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.815985706430962
+ public: {'centers_str': ' centers[0] = (0.0299, 0.0275)\n centers[1] = (0.0149, 0.3749)\n centers[2] = (0.1021, 0.4970)\n centers[3] = (0.0962, 0.7033)\n centers[4] = (0.0935, 0.8942)\n centers[5] = (0.3742, 0.0202)\n centers[6] = (0.3949, 0.3874)\n centers[7] = (0.3025, 0.4996)\n centers[8] = (0.2248, 0.7144)\n centers[9] = (0.2964, 0.8907)\n centers[10] = (0.5038, 0.1012)\n centers[11] = (0.4875, 0.2976)\n centers[12] = (0.4544, 0.4790)\n centers[13] = (0.3895, 0.6722)\n centers[14] = (0.4565, 0.9410)\n centers[15] = (0.7013, 0.0497)\n centers[16] = (0.7011, 0.1639)\n centers[17] = (0.6576, 0.3754)\n centers[18] = (0.7474, 0.7511)\n centers[19] = (0.6927, 1.0000)\n centers[20] = (0.9024, 0.0846)\n centers[21] = (0.8897, 0.2891)\n centers[22] = (0.9373, 0.4741)\n centers[23] = (1.0000, 0.6956)\n centers[24] = (0.9635, 0.9624)\n centers[25] = (0.2203, 0.2253)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.815985706430962}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/packing_viz.png
+ execution_time_mean: 31.111416799016297
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..80e298fa784ed17f8335fba09328f733d5e9bf90
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.815985706430962,
+ "public": {
+ "centers_str": " centers[0] = (0.0299, 0.0275)\n centers[1] = (0.0149, 0.3749)\n centers[2] = (0.1021, 0.4970)\n centers[3] = (0.0962, 0.7033)\n centers[4] = (0.0935, 0.8942)\n centers[5] = (0.3742, 0.0202)\n centers[6] = (0.3949, 0.3874)\n centers[7] = (0.3025, 0.4996)\n centers[8] = (0.2248, 0.7144)\n centers[9] = (0.2964, 0.8907)\n centers[10] = (0.5038, 0.1012)\n centers[11] = (0.4875, 0.2976)\n centers[12] = (0.4544, 0.4790)\n centers[13] = (0.3895, 0.6722)\n centers[14] = (0.4565, 0.9410)\n centers[15] = (0.7013, 0.0497)\n centers[16] = (0.7011, 0.1639)\n centers[17] = (0.6576, 0.3754)\n centers[18] = (0.7474, 0.7511)\n centers[19] = (0.6927, 1.0000)\n centers[20] = (0.9024, 0.0846)\n centers[21] = (0.8897, 0.2891)\n centers[22] = (0.9373, 0.4741)\n centers[23] = (1.0000, 0.6956)\n centers[24] = (0.9635, 0.9624)\n centers[25] = (0.2203, 0.2253)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.815985706430962
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/results/packing_viz.png",
+ "execution_time_mean": 31.111416799016297,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2fc2729cb30cf7490ff81bd7ebb131afac869fa6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_46/search_replace.txt
@@ -0,0 +1,51 @@
+
+aggressive_initial_explosion
+
+
+The current simulation results in a sum of radii (1.93) that is significantly worse than a simple 5x5 packing (2.5), indicating the simulation is getting trapped in a deep and undesirable local minimum originating from the initial grid structure. The force-directed simulation is not energetic enough to break the grid's symmetry and rearrange the circles into a more globally optimal, amorphous configuration.
+
+To address this, I propose an "initial explosion" strategy by aggressively tuning the main simulation phase hyperparameters. The key changes are:
+
+1. **Drastically increase `initial_growth_pressure` from 1.018 to 1.20.** This will make the simulation "see" circles as 20% larger than their current maximum possible size at the beginning. This generates massive initial overlap forces, designed to violently push the circles out of their rigid grid positions.
+
+2. **Increase `learning_rate_main` from 0.01 to 0.02.** A higher learning rate allows the centers to take larger steps in response to the amplified forces, facilitating a faster escape from the initial configuration.
+
+3. **Increase `iterations_main` from 1200 to 1500.** A more chaotic simulation requires more time to settle into a new, stable, and high-quality arrangement.
+
+4. **Increase `initial_perturbation_scale` from 0.005 to 0.01.** A larger initial random nudge further helps in breaking the grid's symmetry from the very start.
+
+5. **Refine the fine-tuning phase.** With a more chaotic main phase, the fine-tuning needs to be more patient. I've increased `fine_tune_iterations` to 400 and decreased `fine_tune_learning_rate` to 0.0005 for more precise final adjustments.
+
+This coordinated set of changes aims to inject enough energy into the system to overcome the activation barrier of the initial grid configuration and explore a much wider, more promising area of the solution space.
+
+
+<<<<<<< SEARCH
+ optimizer = CirclePackingOptimizer(
+ n=26,
+ iterations_main=1200, # Increased for more thorough exploration
+ learning_rate_main=0.01,
+ initial_growth_pressure=1.018, # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure=1.0001, # Closer to 1.0 for precise packing at end of main phase
+ wall_repulsion_strength=0.3, # Slightly adjusted wall repulsion
+ fine_tune_iterations=300, # Increased fine-tune iterations for precision
+ fine_tune_learning_rate=0.0008, # Slower fine-tuning for high precision
+ fine_tune_growth_pressure_start=1.0005, # Small growth pressure in fine-tune (Recommendation 3)
+ fine_tune_growth_pressure_end=1.00001, # Almost zero growth pressure at the end of fine-tune (Recommendation 3)
+ initial_perturbation_scale=0.005 # Small random perturbation to initial grid (Recommendation 4)
+ )
+=======
+ optimizer = CirclePackingOptimizer(
+ n=26,
+ iterations_main=1500, # More iterations to settle after explosion
+ learning_rate_main=0.02, # Higher LR for bigger steps
+ initial_growth_pressure=1.20, # Drastically higher initial pressure for "explosion"
+ final_growth_pressure=1.001, # End pressure can be slightly higher
+ wall_repulsion_strength=0.3, # Keep this, seems reasonable
+ fine_tune_iterations=400, # More fine-tuning needed after a chaotic main phase
+ fine_tune_learning_rate=0.0005, # Slower fine-tuning for precision
+ fine_tune_growth_pressure_start=1.001, # Slightly more pressure in fine-tune start
+ fine_tune_growth_pressure_end=1.00001, # Kept for final precision
+ initial_perturbation_scale=0.01 # Slightly larger perturbation to help break symmetry
+ )
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..89e33729887b9cee25bb94219bc9558d914e53a9
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..8af73d0f1b740d95896cd9c8a8b7e64db3252009
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/edit.diff
@@ -0,0 +1,254 @@
+--- a/original.py
++++ b/original.py
+@@ -1,223 +1,223 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+- # b) Wall repulsion
+- for i in range(self.n):
+- overlap_l = pressured_radii[i] - self.centers[i, 0]
+- if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+-
+- overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+- if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+-
+- overlap_b = pressured_radii[i] - self.centers[i, 1]
+- if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+-
+- overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+- if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
++ # b) Wall repulsion (vectorized)
++ # Left wall (x=0)
++ overlap_l = pressured_radii - self.centers[:, 0]
++ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
++ # Right wall (x=1)
++ overlap_r = (self.centers[:, 0] + pressured_radii) - 1.0
++ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
++ # Bottom wall (y=0)
++ overlap_b = pressured_radii - self.centers[:, 1]
++ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
++ # Top wall (y=1)
++ overlap_t = (self.centers[:, 1] + pressured_radii) - 1.0
++ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b) Wall repulsion (based on actual radii)
+- for i in range(self.n):
+- overlap_l = current_radii[i] - self.centers[i, 0]
+- if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+-
+- overlap_r = (self.centers[i, 0] + current_radii[i]) - 1.0
+- if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+-
+- overlap_b = current_radii[i] - self.centers[i, 1]
+- if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+-
+- overlap_t = (self.centers[i, 1] + current_radii[i]) - 1.0
+- if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
++ # b) Wall repulsion (vectorized, based on actual radii)
++ # Left wall (x=0)
++ overlap_l = current_radii - self.centers[:, 0]
++ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
++ # Right wall (x=1)
++ overlap_r = (self.centers[:, 0] + current_radii) - 1.0
++ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
++ # Bottom wall (y=0)
++ overlap_b = current_radii - self.centers[:, 1]
++ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
++ # Top wall (y=1)
++ overlap_t = (self.centers[:, 1] + current_radii) - 1.0
++ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+ def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary.
+ # We introduce an annealing schedule for growth pressure to break out of
+ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+- 'sim_iter': 1000, # Increased iterations for thorough refinement.
++ 'sim_iter': 2000, # Doubled iterations for more thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+- 'fine_tune_iter': 200, # Increased iterations for final settlement.
++ 'fine_tune_iter': 400, # Doubled iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..4d61b633806ab6c86af21ff700f268c84f806ce7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/main.py
@@ -0,0 +1,223 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized)
+ # Left wall (x=0)
+ overlap_l = pressured_radii - self.centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (self.centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - self.centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (self.centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - self.centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (self.centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - self.centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (self.centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary.
+ # We introduce an annealing schedule for growth pressure to break out of
+ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+ 'sim_iter': 2000, # Doubled iterations for more thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 400, # Doubled iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..366062572268f1157bc37130701427d81ac9bdd1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/original.py
@@ -0,0 +1,223 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion
+ for i in range(self.n):
+ overlap_l = pressured_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(self.n):
+ overlap_l = current_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = current_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary.
+ # We introduce an annealing schedule for growth pressure to break out of
+ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+ 'sim_iter': 1000, # Increased iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 200, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..9441142e4d91a5d8d670ad967f6ff24804487a6f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results
+Run 1/1 completed in 6.81 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9558479358284226
+ public: {'centers_str': ' centers[0] = (0.0945, 0.0950)\n centers[1] = (0.3103, 0.0897)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8988, 0.1012)\n centers[5] = (0.0855, 0.3145)\n centers[6] = (0.3049, 0.3053)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.6999, 0.4937)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.2933, 0.6969)\n centers[17] = (0.4737, 0.6931)\n centers[18] = (0.7039, 0.6847)\n centers[19] = (0.9174, 0.7009)\n centers[20] = (0.0912, 0.9000)\n centers[21] = (0.2932, 0.8893)\n centers[22] = (0.5096, 0.8947)\n centers[23] = (0.7070, 0.9081)\n centers[24] = (0.8995, 0.8982)\n centers[25] = (0.2048, 0.1954)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9558479358284226}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/packing_viz.png
+ execution_time_mean: 6.807687510270625
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..ee86de035d9c7aeac969483e22be0ab24526c334
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9558479358284226,
+ "public": {
+ "centers_str": " centers[0] = (0.0945, 0.0950)\n centers[1] = (0.3103, 0.0897)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8988, 0.1012)\n centers[5] = (0.0855, 0.3145)\n centers[6] = (0.3049, 0.3053)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.6999, 0.4937)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.2933, 0.6969)\n centers[17] = (0.4737, 0.6931)\n centers[18] = (0.7039, 0.6847)\n centers[19] = (0.9174, 0.7009)\n centers[20] = (0.0912, 0.9000)\n centers[21] = (0.2932, 0.8893)\n centers[22] = (0.5096, 0.8947)\n centers[23] = (0.7070, 0.9081)\n centers[24] = (0.8995, 0.8982)\n centers[25] = (0.2048, 0.1954)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9558479358284226
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/results/packing_viz.png",
+ "execution_time_mean": 6.807687510270625,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3b39608f99256b5d06e34f857aa875a7e9ec653b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_47/search_replace.txt
@@ -0,0 +1,112 @@
+Based on my analysis of the previous program versions and the current implementation, the most impactful changes will involve reintroducing performance optimizations that were present in higher-scoring predecessors. Specifically, vectorizing the wall repulsion forces and using the gained performance to increase the number of simulation iterations. This strategy directly led to a score of 1.96 previously and should be highly effective here.
+
+I will propose three sequential edits:
+1. Vectorize the wall repulsion calculation in the main refinement simulation.
+2. Vectorize the wall repulsion calculation in the fine-tuning phase.
+3. Double the number of simulation iterations in both phases to leverage the new efficiency.
+
+
+vectorize_refinement_wall_repulsion
+
+
+To improve simulation performance, the wall repulsion calculation in the `_run_refinement_simulation` method is converted from a Python `for` loop to a vectorized NumPy implementation. This change significantly reduces the computational overhead of each simulation step by leveraging optimized low-level code, enabling more iterations within the same time budget for a more thorough exploration of the solution space.
+
+
+<<<<<<< SEARCH
+ # b) Wall repulsion
+ for i in range(self.n):
+ overlap_l = pressured_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+=======
+ # b) Wall repulsion (vectorized)
+ # Left wall (x=0)
+ overlap_l = pressured_radii - self.centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (self.centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - self.centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (self.centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+>>>>>>> REPLACE
+
+
+vectorize_finetuning_wall_repulsion
+
+
+Following the optimization in the refinement phase, this edit vectorizes the wall repulsion calculation within the `_run_fine_tuning` method. Replacing the `for` loop with a NumPy equivalent ensures that the final settling phase is also computationally efficient, allowing for more fine-grained adjustments without a significant performance penalty. This keeps the simulation logic consistent and optimized across both phases.
+
+
+<<<<<<< SEARCH
+ # b) Wall repulsion (based on actual radii)
+ for i in range(self.n):
+ overlap_l = current_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = current_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+=======
+ # b) Wall repulsion (vectorized, based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - self.centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (self.centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - self.centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (self.centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+>>>>>>> REPLACE
+
+
+increase_simulation_iterations
+
+
+To leverage the performance gains from vectorizing the wall repulsion calculations, the number of iterations for both simulation phases is doubled. `sim_iter` is increased from 1000 to 2000, and `fine_tune_iter` from 200 to 400. This provides the force-directed algorithm with more time to explore the configuration space and settle into a more optimal, higher-density packing, directly translating the new computational efficiency into a better potential result.
+
+
+<<<<<<< SEARCH
+ packer_config = {
+ 'sim_iter': 1000, # Increased iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 200, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+=======
+ packer_config = {
+ 'sim_iter': 2000, # Doubled iterations for more thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 400, # Doubled iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..beff29dd7ee16c0b2ae4e58cc912e5c8bd39417f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/edit.diff
@@ -0,0 +1,240 @@
+--- a/original.py
++++ b/original.py
+@@ -1,199 +1,218 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ This version is a crossover, combining the high-level strategy of the best-performing
+ parent (multi-start, two-phase simulation, annealed growth pressure) with the
+ efficient vectorized force calculations from the inspiration program.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- # Increased iterations due to vectorized efficiency
+- iterations = 750
++ # Tuned hyperparameters based on successful prior implementations
++ iterations = 1200 # Increased for more thorough exploration
+ learning_rate = 0.01
+- wall_repulsion_strength = 0.5
+- initial_growth_pressure = 1.01
+- final_growth_pressure = 1.001
++ wall_repulsion_strength = 0.3 # Adjusted wall repulsion for better boundary interaction
++ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
++ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
++
++ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
++ perturbation_scale = 0.002 # A small fraction of the spacing
++ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
++
+ current_centers[25] = initial_pos_26th_circle
++
++ # Ensure all centers stay within bounds after initial setup and perturbation
++ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+- # Anneal growth pressure
+- current_growth_pressure = initial_growth_pressure - \
+- (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
++ # Anneal growth pressure quadratically (Recommendation 2 from previous G37)
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * \
++ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 100 # Increased due to efficiency
+- fine_tune_learning_rate = 0.001
++ fine_tune_iterations = 300 # Increased fine-tune iterations for precision
++ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
++ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
++ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+
+- for _ in range(fine_tune_iterations):
++ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+- # --- Vectorized Force Calculation (no growth pressure) ---
++ # Anneal subtle growth pressure (linear decay for stability)
++ current_finetune_growth_pressure = finetune_growth_pressure_start - \
++ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
++ (sim_iter_ft / fine_tune_iterations)
++ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
++
++ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
++ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+- # b. Wall forces
++ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+- overlap_bottom = radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
++ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
++ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+- centers_for_sim += forces * fine_tune_learning_rate
++ # Anneal learning rate for fine-tuning (linear decay)
++ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
++ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..15e2065d10b82e1a24064c8969333788774107fb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/main.py
@@ -0,0 +1,218 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters based on successful prior implementations
+ iterations = 1200 # Increased for more thorough exploration
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.3 # Adjusted wall repulsion for better boundary interaction
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically (Recommendation 2 from previous G37)
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 300 # Increased fine-tune iterations for precision
+ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d8b70cd02cf01ce1f4cb7c08d8829ad74d35c7a2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/original.py
@@ -0,0 +1,199 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9195bc3d745fb29e5c0cee533a2ad75c7fdb2da2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_48/search_replace.txt
@@ -0,0 +1,194 @@
+
+
+This edit incorporates several improvements based on analysis of previous successful programs and recommendations:
+1. **Updated Hyperparameters for Main Simulation:** The `iterations`, `wall_repulsion_strength`, `initial_growth_pressure`, and `final_growth_pressure` are updated to values that yielded higher scores in previous generations (specifically, a version that scored 1.94). This includes increasing main iterations to 1200 for more thorough exploration and adjusting wall repulsion.
+2. **Quadratic Growth Pressure Annealing in Main Phase:** The annealing schedule for `current_growth_pressure` in the main simulation loop is changed from linear to quadratic. This provides a more aggressive initial exploration with higher pressure, gradually decaying for smoother convergence, which was a feature of higher-scoring programs.
+3. **Initial Random Perturbation (Recommendation 4):** A small, normally distributed random perturbation is added to the initial 5x5 grid positions (first 25 circles). This helps to break perfect symmetries and allows the simulation to explore a wider configuration space, potentially escaping local minima that a perfectly regular start might get stuck in. The `perturbation_scale` is set to 0.002, a small fraction of the grid spacing.
+4. **Bounds Clipping:** Centers are clipped to `[0.0, 1.0]` after initial setup and perturbation to ensure they remain within the unit square.
+
+
+<<<<<<< SEARCH
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+=======
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters based on successful prior implementations
+ iterations = 1200 # Increased for more thorough exploration
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.3 # Adjusted wall repulsion for better boundary interaction
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically (Recommendation 2 from previous G37)
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+>>>>>>> REPLACE
+
+
+
+
+This edit re-introduces and refines the fine-tuning phase by:
+1. **Re-introducing Annealed Growth Pressure (Recommendation 2):** The fine-tuning phase now includes a subtle, annealed growth pressure. This helps to resolve minor overlaps and nudge circles into slightly tighter configurations that pure repulsion might miss, ensuring precise packing in the final stages. The `finetune_growth_pressure_start` is 1.0005, decaying linearly to `finetune_growth_pressure_end` at 1.00001.
+2. **Increased Fine-Tune Iterations and Adjusted Learning Rate:** The `fine_tune_iterations` are increased to 300 (from 100) and `fine_tune_learning_rate` adjusted to 0.0008 for more precise settling, consistent with successful prior versions.
+3. **Linear Learning Rate Annealing:** A linear annealing schedule is applied to the learning rate during fine-tuning, allowing for larger adjustments initially and very small, precise movements towards the end.
+4. **Consistency in Wall Forces:** The wall repulsion forces in the fine-tuning phase now correctly use the `inflated_radii_ft` (which incorporates the fine-tune growth pressure), maintaining consistency with the overall force model.
+
+
+<<<<<<< SEARCH
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+=======
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 300 # Increased fine-tune iterations for precision
+ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+>>>>>>> REPLACE
+
+
+
+
+This edit ensures that the wall repulsion forces in the fine-tuning phase correctly use the `inflated_radii_ft`. This is crucial for consistency with the introduced fine-tune growth pressure and for accurately simulating the interaction with boundaries. A linear learning rate annealing is also applied to `centers_for_sim` updates in this phase for better convergence.
+
+
+<<<<<<< SEARCH
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+=======
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..ec48447922ae66e5f1a0c0e7748b3cff256928cc
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/edit.diff
@@ -0,0 +1,333 @@
+--- a/original.py
++++ b/original.py
+@@ -1,173 +1,236 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ Improved by combining insights from high-scoring prior programs:
+ - Optimized initial placement of the 26th circle.
+ - Reduced initial perturbation noise.
+ - Tuned learning rate for fine-grained adjustments.
+ - Increased iterations for radius calculation convergence.
+ - Robust handling of very close circle centers in radius calculation.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+- to find an optimal arrangement of centers.
++ to find an optimal arrangement of centers. This version incorporates:
++ - Multi-start initial placement for the 26th circle.
++ - A two-phase force-directed simulation (annealed growth pressure + fine-tuning).
++ - Vectorized wall repulsion for efficiency.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+- # --- Hyperparameters for the simulation (tuned from high-scoring program) ---
+- iterations = 500 # Increased iterations for better convergence
+- learning_rate = 0.02 # A balanced learning rate for exploration.
+- wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+- growth_pressure = 1.005 # Key parameter: gentle expansion pressure to fill gaps.
+-
+- # --- Initialization ---
+- # Start with a 5x5 grid plus one interstitial circle. This is a proven high-potential starting configuration.
+- centers = np.zeros((n, 2))
++ # --- Hyperparameters for the simulation ---
++ sim_iter = 1500 # Increased iterations for thorough refinement.
++ learning_rate = 0.02 # A balanced learning rate for exploration.
++ wall_strength = 0.8 # Stronger walls to better utilize boundary space.
++ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
++ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
++ fine_tune_iter = 500 # Increased iterations for final settlement.
++ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
++ radius_calc_sim_iter = 150 # Iterations for radii during simulation (lower for speed).
++ radius_calc_final_iter = 500 # Iterations for final radii calculation (higher for precision).
++
++
++ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
++ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- centers[k, 0] = (i + 0.5) * spacing
+- centers[k, 1] = (j + 0.5) * spacing
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ base_centers[k, 0] = (i + 0.5) * spacing
++ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+- # Place the 26th circle in the interstitial void at (0.2, 0.2)
+- centers[25] = [spacing, spacing]
+-
+- # --- Simulation Loop ---
+- # The simulation refines center positions using a force-directed layout algorithm.
+- # It uses a "growth pressure" concept to actively seek more efficient packings.
+- for sim_iter in range(iterations):
+- # 1. Calculate the maximum non-overlapping radii for the current positions.
+- radii = compute_max_radii(centers)
+-
+- # 2. Artificially inflate radii to create an expansion 'pressure'.
+- # This is the key to escaping local minima and finding denser packings.
+- pressured_radii = radii * growth_pressure
+-
+- # 3. Calculate forces to push centers to a better configuration.
+- forces = np.zeros((n, 2))
+-
+- # a. Circle-to-circle repulsion based on artificial overlap
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers[i] - centers[j]
+- dist = np.linalg.norm(vec)
+-
+- if dist < 1e-9:
+- continue
+-
+- # Calculate overlap using the inflated, 'pressured' radii
+- overlap = pressured_radii[i] + pressured_radii[j] - dist
+-
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec
+-
+- # b. Wall repulsion forces, also using pressured radii
+- for i in range(n):
+- # Left wall
+- overlap_left = pressured_radii[i] - centers[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers[i, 0] + pressured_radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = pressured_radii[i] - centers[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers[i, 1] + pressured_radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
+-
+- # 4. Update center positions with an annealing learning rate for stability.
+- current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+- centers += forces * current_lr
+-
+- # 5. Enforce hard boundary constraints.
+- centers = np.clip(centers, 0.0, 1.0)
+-
+- # --- Finalization ---
+- # After the simulation, compute the final, precise radii for the optimized centers.
+- final_radii = compute_max_radii(centers)
+- final_centers = centers
+-
+- return final_centers, final_radii
+-
+-
+-def compute_max_radii(centers):
++ # Strategic candidate placements for the 26th circle.
++ # Includes interstitial points and corner-offset points.
++ candidate_extra_positions = [
++ [spacing, spacing], # (0.2, 0.2)
++ [spacing, 0.5], # (0.2, 0.5)
++ [spacing, 1 - spacing], # (0.2, 0.8)
++ [0.5, spacing], # (0.5, 0.2)
++ [0.5, 0.5], # (0.5, 0.5) - Center
++ [0.5, 1 - spacing], # (0.5, 0.8)
++ [1 - spacing, spacing], # (0.8, 0.2)
++ [1 - spacing, 0.5], # (0.8, 0.5)
++ [1 - spacing, 1 - spacing], # (0.8, 0.8)
++ [0.05, 0.05], # Corner offsets
++ [0.05, 0.95],
++ [0.95, 0.05],
++ [0.95, 0.95]
++ ]
++
++ best_sum_radii = -1.0
++ best_centers = None
++ best_radii = None
++
++ # Iterate through candidate positions for the 26th circle
++ for extra_pos in candidate_extra_positions:
++ current_centers = np.zeros((n, 2))
++ current_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
++ current_centers[n-1] = np.array(extra_pos)
++
++ centers_for_sim = current_centers.copy() # Work on a copy for the simulation
++
++ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
++ for i_iter in range(sim_iter):
++ # Anneal growth pressure quadratically.
++ progress = i_iter / sim_iter
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++
++ # 1. Calculate the maximum non-overlapping radii for the current positions.
++ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
++
++ # 2. Artificially inflate radii to create an expansion 'pressure'.
++ pressured_radii = current_radii * current_growth_pressure
++
++ forces = np.zeros_like(centers_for_sim)
++
++ # 3. Calculate repulsion forces based on artificial overlaps.
++ # a) Circle-to-circle repulsion (vectorized)
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
++
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = radii_sums - dists
++ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
++ np.fill_diagonal(overlaps, 0) # No self-repulsion
++
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ # b) Wall repulsion (vectorized, based on pressured radii)
++ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
++ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
++ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
++ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
++
++ # 4. Update center positions with a decaying learning rate for stability.
++ lr = learning_rate * (1.0 - progress)**2 # Annealing learning rate
++ centers_for_sim += forces * lr
++
++ # 5. Enforce hard boundary constraints.
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
++ for _ in range(fine_tune_iter):
++ # 1. Calculate radii for the current positions. NO growth pressure.
++ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
++
++ forces = np.zeros_like(centers_for_sim)
++
++ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
++ # a) Circle-to-circle repulsion (vectorized)
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
++ overlaps = radii_sums - dists
++ overlaps[overlaps < 0] = 0.0
++ np.fill_diagonal(overlaps, 0)
++
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ # b) Wall repulsion (vectorized, based on actual radii)
++ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 0])
++ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + current_radii) - 1.0)
++ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 1])
++ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + current_radii) - 1.0)
++
++ # 3. Update positions with small learning rate.
++ centers_for_sim += forces * fine_tune_lr
++
++ # 4. Enforce hard boundary constraints.
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # After both simulation and fine-tuning, evaluate this configuration
++ current_radii_final = compute_max_radii(centers_for_sim, iterations=radius_calc_final_iter)
++ current_sum_radii = np.sum(current_radii_final)
++
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_centers = centers_for_sim.copy()
++ best_radii = current_radii_final.copy()
++
++ # Return the best configuration found
++ # Fallback if no valid configuration was found (should not happen with good candidates)
++ if best_centers is None:
++ # Revert to a safe default if no better solution was found
++ final_centers = np.zeros((n, 2))
++ final_centers[:n-1] = base_centers
++ final_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
++ final_radii = compute_max_radii(final_centers, iterations=radius_calc_final_iter)
++ return final_centers, final_radii
++ else:
++ return best_centers, best_radii
++
++
++def compute_max_radii(centers, iterations=500): # Changed default to 500 for final precision
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
++ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence, matching high-scoring programs.
+- for _ in range(250):
++ for _ in range(iterations): # Use the passed iterations argument
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+- # If centers are practically the same, they cannot both have positive radius.
+- # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+- continue # Skip to next pair
+-
+- # If current radii would cause overlap
++ continue
++
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+- # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+- # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+- return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
++ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce1365f1f4fb5a6f2a7816f72c8f7b172ccb3683
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/main.py
@@ -0,0 +1,236 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers. This version incorporates:
+ - Multi-start initial placement for the 26th circle.
+ - A two-phase force-directed simulation (annealed growth pressure + fine-tuning).
+ - Vectorized wall repulsion for efficiency.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ sim_iter = 1500 # Increased iterations for thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 500 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_calc_sim_iter = 150 # Iterations for radii during simulation (lower for speed).
+ radius_calc_final_iter = 500 # Iterations for final radii calculation (higher for precision).
+
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset points.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+ current_centers[n-1] = np.array(extra_pos)
+
+ centers_for_sim = current_centers.copy() # Work on a copy for the simulation
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically.
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers_for_sim)
+
+ # 3. Calculate repulsion forces based on artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on pressured radii)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = learning_rate * (1.0 - progress)**2 # Annealing learning rate
+ centers_for_sim += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
+
+ forces = np.zeros_like(centers_for_sim)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation and fine-tuning, evaluate this configuration
+ current_radii_final = compute_max_radii(centers_for_sim, iterations=radius_calc_final_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy()
+ best_radii = current_radii_final.copy()
+
+ # Return the best configuration found
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # Revert to a safe default if no better solution was found
+ final_centers = np.zeros((n, 2))
+ final_centers[:n-1] = base_centers
+ final_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers, iterations=radius_calc_final_iter)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers, iterations=500): # Changed default to 500 for final precision
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(iterations): # Use the passed iterations argument
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..46e6498ef4412e636b351042d79badbab6737584
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/original.py
@@ -0,0 +1,173 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation (tuned from high-scoring program) ---
+ iterations = 500 # Increased iterations for better convergence
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ growth_pressure = 1.005 # Key parameter: gentle expansion pressure to fill gaps.
+
+ # --- Initialization ---
+ # Start with a 5x5 grid plus one interstitial circle. This is a proven high-potential starting configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the interstitial void at (0.2, 0.2)
+ centers[25] = [spacing, spacing]
+
+ # --- Simulation Loop ---
+ # The simulation refines center positions using a force-directed layout algorithm.
+ # It uses a "growth pressure" concept to actively seek more efficient packings.
+ for sim_iter in range(iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ radii = compute_max_radii(centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the key to escaping local minima and finding denser packings.
+ pressured_radii = radii * growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration.
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion based on artificial overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Calculate overlap using the inflated, 'pressured' radii
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using pressured radii
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 4. Update center positions with an annealing learning rate for stability.
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 5. Enforce hard boundary constraints.
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, compute the final, precise radii for the optimized centers.
+ final_radii = compute_max_radii(centers)
+ final_centers = centers
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence, matching high-scoring programs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1038a20758fd5c08717e025dbabffec2bb49be8c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_49/search_replace.txt
@@ -0,0 +1,396 @@
+
+flexible_radii_calc
+
+
+
+The `compute_max_radii` function is a critical component used both during the iterative simulation and for the final evaluation. Previously, it had a fixed iteration count (250). To improve performance and accuracy, this edit makes the `iterations` parameter configurable. This allows for fewer iterations (e.g., 150) during the simulation phases (where approximate radii are sufficient and speed is crucial), and more iterations (e.g., 500) for the final, precise radius calculation to ensure maximum accuracy for the sum of radii. The default iteration count is also increased to 500 for the final precision.
+
+
+
+<<<<<<< SEARCH
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence, matching high-scoring programs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ # If centers are practically the same, they cannot both have positive radius.
+ # Force their radii to zero to resolve the severe overlap.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue # Skip to next pair
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should not happen with this logic, but for safety)
+=======
+def compute_max_radii(centers, iterations=500): # Changed default to 500 for final precision
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(iterations): # Use the passed iterations argument
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+>>>>>>> REPLACE
+
+
+
+multi_start_two_phase_vectorized
+
+
+
+This edit significantly refactors the `construct_packing` function to integrate several high-impact strategies derived from past successful programs:
+
+1. **Multi-Start Strategy for the 26th Circle:** Instead of fixing the 26th circle's initial position, the algorithm now explores a predefined set of promising candidate locations (interstitial voids and corner-offset points) within the 5x5 grid. The full simulation and fine-tuning process is run for each candidate, and the best resulting packing is selected. This helps in escaping local minima caused by a single, suboptimal initial placement.
+
+2. **Two-Phase Force-Directed Simulation:** The previous single-phase simulation is replaced with a more robust two-phase approach:
+ * **Phase 1 (Refinement with Annealed Growth Pressure):** Circles are initially "melted" using a higher `initial_growth_pressure`, which gradually decreases (`final_growth_pressure`) over many iterations (`sim_iter`). This annealing schedule allows circles to explore the configuration space more broadly before settling, effectively pushing them to fill voids and maximize their radii. The learning rate for center updates also anneals quadratically.
+ * **Phase 2 (Fine-Tuning without Growth Pressure):** A subsequent phase with a smaller number of iterations (`fine_tune_iter`) and a constant, very small learning rate (`fine_tune_lr`) is introduced. This phase operates without growth pressure, allowing the circles to precisely settle into their non-overlapping, maximal-radius positions, resolving any minor overlaps or jitters from the first phase.
+
+3. **Vectorized Wall Repulsion:** The calculation of forces exerted by the square boundaries is now fully vectorized using NumPy operations. This replaces the less efficient `for` loops, leading to a significant performance improvement in the simulation loop.
+
+4. **Optimized Hyperparameters:** The iteration counts for the simulation phases (`sim_iter`, `fine_tune_iter`) and for radius calculations (`radius_calc_sim_iter`, `radius_calc_final_iter`) have been tuned. `initial_growth_pressure` and `final_growth_pressure` are set to encourage effective packing.
+
+These changes collectively enhance the algorithm's ability to thoroughly explore the solution space, converge to higher-quality packings, and do so more efficiently.
+
+
+
+<<<<<<< SEARCH
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation (tuned from high-scoring program) ---
+ iterations = 500 # Increased iterations for better convergence
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ growth_pressure = 1.005 # Key parameter: gentle expansion pressure to fill gaps.
+
+ # --- Initialization ---
+ # Start with a 5x5 grid plus one interstitial circle. This is a proven high-potential starting configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in the interstitial void at (0.2, 0.2)
+ centers[25] = [spacing, spacing]
+
+ # --- Simulation Loop ---
+ # The simulation refines center positions using a force-directed layout algorithm.
+ # It uses a "growth pressure" concept to actively seek more efficient packings.
+ for sim_iter in range(iterations):
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ radii = compute_max_radii(centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the key to escaping local minima and finding denser packings.
+ pressured_radii = radii * growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration.
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion based on artificial overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ # Calculate overlap using the inflated, 'pressured' radii
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces, also using pressured radii
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 4. Update center positions with an annealing learning rate for stability.
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers += forces * current_lr
+
+ # 5. Enforce hard boundary constraints.
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, compute the final, precise radii for the optimized centers.
+ final_radii = compute_max_radii(centers)
+ final_centers = centers
+
+ return final_centers, final_radii
+=======
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers. This version incorporates:
+ - Multi-start initial placement for the 26th circle.
+ - A two-phase force-directed simulation (annealed growth pressure + fine-tuning).
+ - Vectorized wall repulsion for efficiency.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ sim_iter = 1500 # Increased iterations for thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 500 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_calc_sim_iter = 150 # Iterations for radii during simulation (lower for speed).
+ radius_calc_final_iter = 500 # Iterations for final radii calculation (higher for precision).
+
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset points.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+ current_centers[n-1] = np.array(extra_pos)
+
+ centers_for_sim = current_centers.copy() # Work on a copy for the simulation
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically.
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers_for_sim)
+
+ # 3. Calculate repulsion forces based on artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on pressured radii)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = learning_rate * (1.0 - progress)**2 # Annealing learning rate
+ centers_for_sim += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
+
+ forces = np.zeros_like(centers_for_sim)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation and fine-tuning, evaluate this configuration
+ current_radii_final = compute_max_radii(centers_for_sim, iterations=radius_calc_final_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy()
+ best_radii = current_radii_final.copy()
+
+ # Return the best configuration found
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # Revert to a safe default if no better solution was found
+ final_centers = np.zeros((n, 2))
+ final_centers[:n-1] = base_centers
+ final_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers, iterations=radius_calc_final_iter)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e87ec242c38f3a848e6780094bf8499890c00cf7
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..13a2f0ac2468ddfae7275c157ae8133ecbbd6879
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/edit.diff
@@ -0,0 +1,190 @@
+--- a/original.py
++++ b/original.py
+@@ -1,95 +1,155 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
++"""
++Physical simulation-based circle packing for n=26 circles.
++This algorithm iteratively refines circle positions to maximize radii sum.
++"""
+
+ import numpy as np
+
+-
+ def construct_packing():
+ """
+- Construct a specific arrangement of 26 circles in a unit square
+- that attempts to maximize the sum of their radii.
++ Constructs a packing of 26 circles by simulating physical repulsion forces
++ to find an optimal arrangement of centers.
+
+ Returns:
+- Tuple of (centers, radii, sum_of_radii)
++ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+- sum_of_radii: Sum of all radii
+ """
+- # Initialize arrays for 26 circles
+ n = 26
++
++ # --- Hyperparameters for the simulation ---
++ iterations = 250
++ learning_rate = 0.1
++ wall_repulsion_strength = 0.5
++
++ # --- Initialization ---
++ # Start with a perturbed grid. This is a good initial guess,
++ # and the perturbation helps break symmetry and escape a rigid structure.
+ centers = np.zeros((n, 2))
+-
+- # Place 25 circles in a 5x5 grid
+- # This provides a more even distribution and better use of space
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+-
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
++ # Place the 26th circle somewhere in the middle to start.
++ centers[25] = [0.51, 0.49]
++
++ # Add small random noise to break grid symmetry
++ np.random.seed(42) # for reproducibility
++ centers += np.random.normal(0, 0.01, centers.shape)
++ centers = np.clip(centers, 0.01, 0.99)
+
+- # Place the 26th circle in a corner, slightly offset to allow for radius.
+- # This location is chosen to utilize corner space efficiently,
+- # as the grid circles are not directly in the corners.
+- centers[25] = [0.05, 0.05]
++ # --- Simulation Loop ---
++ for k in range(iterations):
++ # 1. Calculate radii for the current center configuration
++ radii = compute_max_radii(centers)
++
++ # 2. Calculate forces to push centers to a better configuration
++ forces = np.zeros((n, 2))
++
++ # a. Circle-to-circle repulsion forces
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers[i] - centers[j]
++ dist = np.linalg.norm(vec)
++ if dist < 1e-9: continue
+
+- # No need to clip centers, as they are intentionally placed within (0,1) range
+- # and compute_max_radii handles boundary constraints.
++ overlap = radii[i] + radii[j] - dist
++
++ if overlap > 0:
++ # Apply a force proportional to the overlap, along the vector between centers
++ force_magnitude = overlap
++ force_vec = (vec / dist) * force_magnitude
++ forces[i] += force_vec
++ forces[j] -= force_vec
+
+- # Compute maximum valid radii for this configuration
+- radii = compute_max_radii(centers)
+- return centers, radii
++ # b. Wall repulsion forces
++ for i in range(n):
++ # Left wall
++ overlap = radii[i] - centers[i, 0]
++ if overlap > 0:
++ forces[i, 0] += wall_repulsion_strength * overlap
++ # Right wall
++ overlap = (centers[i, 0] + radii[i]) - 1.0
++ if overlap > 0:
++ forces[i, 0] -= wall_repulsion_strength * overlap
++ # Bottom wall
++ overlap = radii[i] - centers[i, 1]
++ if overlap > 0:
++ forces[i, 1] += wall_repulsion_strength * overlap
++ # Top wall
++ overlap = (centers[i, 1] + radii[i]) - 1.0
++ if overlap > 0:
++ forces[i, 1] -= wall_repulsion_strength * overlap
++
++ # 3. Update center positions based on forces
++ # We adjust the learning rate over time (annealing) to help the system settle
++ current_lr = learning_rate * (1.0 - (k / iterations))**2
++ centers += forces * current_lr
++
++ # 4. Enforce boundary constraints
++ centers = np.clip(centers, 0.0, 1.0)
++
++ # --- Finalization ---
++ # After the simulation, do a final radius calculation on the optimized centers
++ final_centers = centers
++ final_radii = compute_max_radii(final_centers)
++
++ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
++ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+- # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+- # Then, limit by distance to other circles
+- # Each pair of circles with centers at distance d can have
+- # sum of radii at most d to avoid overlap
+- # This iterative scaling is a greedy approach and the order can matter,
+- # but for constructor-based approaches, it's a common method.
+- for _ in range(100): # Iterate multiple times to stabilize radii
++ # Then, limit by distance to other circles using iterative proportional scaling
++ for _ in range(100):
++ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+- dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
++ dist = np.linalg.norm(centers[i] - centers[j])
+
+- # If current radii would cause overlap
+- if radii[i] + radii[j] > dist:
++ # If current radii would cause overlap and dist is not zero
++ if radii[i] + radii[j] > dist and dist > 1e-9:
++ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+- return radii
+-
++
++ # If a full pass over all pairs results in no change, radii have stabilized
++ if not updated_in_pass:
++ break
++
++ return np.maximum(radii, 0)
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..e44b192582bde62068817fc151f7caba8f4de343
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/main.py
@@ -0,0 +1,155 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250
+ learning_rate = 0.1
+ wall_repulsion_strength = 0.5
+
+ # --- Initialization ---
+ # Start with a perturbed grid. This is a good initial guess,
+ # and the perturbation helps break symmetry and escape a rigid structure.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle somewhere in the middle to start.
+ centers[25] = [0.51, 0.49]
+
+ # Add small random noise to break grid symmetry
+ np.random.seed(42) # for reproducibility
+ centers += np.random.normal(0, 0.01, centers.shape)
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # --- Simulation Loop ---
+ for k in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap = radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..00c138de9a06c6d9335089fddee91aec818e5abd
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/original.py
@@ -0,0 +1,95 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place 25 circles in a 5x5 grid
+ # This provides a more even distribution and better use of space
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a corner, slightly offset to allow for radius.
+ # This location is chosen to utilize corner space efficiently,
+ # as the grid circles are not directly in the corners.
+ centers[25] = [0.05, 0.05]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ # This iterative scaling is a greedy approach and the order can matter,
+ # but for constructor-based approaches, it's a common method.
+ for _ in range(100): # Iterate multiple times to stabilize radii
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..73d399b7fd35fbd771e39853360d69dd3f388d37
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results
+Run 1/1 completed in 0.77 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.7131570702587058
+ public: {'centers_str': ' centers[0] = (0.1050, 0.0986)\n centers[1] = (0.1065, 0.3152)\n centers[2] = (0.0977, 0.4977)\n centers[3] = (0.1158, 0.7077)\n centers[4] = (0.0953, 0.9054)\n centers[5] = (0.2954, 0.0953)\n centers[6] = (0.3024, 0.2809)\n centers[7] = (0.2828, 0.4944)\n centers[8] = (0.2899, 0.7031)\n centers[9] = (0.2909, 0.8859)\n centers[10] = (0.5147, 0.0977)\n centers[11] = (0.5007, 0.2858)\n centers[12] = (0.4946, 0.5011)\n centers[13] = (0.4885, 0.7038)\n centers[14] = (0.4940, 0.8971)\n centers[15] = (0.6940, 0.1185)\n centers[16] = (0.6999, 0.2894)\n centers[17] = (0.7082, 0.4878)\n centers[18] = (0.7021, 0.6804)\n centers[19] = (0.6867, 0.9020)\n centers[20] = (0.9074, 0.1017)\n centers[21] = (0.8988, 0.2970)\n centers[22] = (0.8852, 0.4928)\n centers[23] = (0.8954, 0.7106)\n centers[24] = (0.9034, 0.8824)\n centers[25] = (0.5132, 0.4861)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.7131570702587058}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/packing_viz.png
+ execution_time_mean: 0.7735453923232853
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..3762fd99afa7a445b14022bdcdb0a52e2cebee12
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.7131570702587058,
+ "public": {
+ "centers_str": " centers[0] = (0.1050, 0.0986)\n centers[1] = (0.1065, 0.3152)\n centers[2] = (0.0977, 0.4977)\n centers[3] = (0.1158, 0.7077)\n centers[4] = (0.0953, 0.9054)\n centers[5] = (0.2954, 0.0953)\n centers[6] = (0.3024, 0.2809)\n centers[7] = (0.2828, 0.4944)\n centers[8] = (0.2899, 0.7031)\n centers[9] = (0.2909, 0.8859)\n centers[10] = (0.5147, 0.0977)\n centers[11] = (0.5007, 0.2858)\n centers[12] = (0.4946, 0.5011)\n centers[13] = (0.4885, 0.7038)\n centers[14] = (0.4940, 0.8971)\n centers[15] = (0.6940, 0.1185)\n centers[16] = (0.6999, 0.2894)\n centers[17] = (0.7082, 0.4878)\n centers[18] = (0.7021, 0.6804)\n centers[19] = (0.6867, 0.9020)\n centers[20] = (0.9074, 0.1017)\n centers[21] = (0.8988, 0.2970)\n centers[22] = (0.8852, 0.4928)\n centers[23] = (0.8954, 0.7106)\n centers[24] = (0.9034, 0.8824)\n centers[25] = (0.5132, 0.4861)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.7131570702587058
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/results/packing_viz.png",
+ "execution_time_mean": 0.7735453923232853,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b784384252d42f8dd5bf5ee38a21a9019cffdecf
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_5/rewrite.txt
@@ -0,0 +1,146 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250
+ learning_rate = 0.1
+ wall_repulsion_strength = 0.5
+
+ # --- Initialization ---
+ # Start with a perturbed grid. This is a good initial guess,
+ # and the perturbation helps break symmetry and escape a rigid structure.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle somewhere in the middle to start.
+ centers[25] = [0.51, 0.49]
+
+ # Add small random noise to break grid symmetry
+ np.random.seed(42) # for reproducibility
+ centers += np.random.normal(0, 0.01, centers.shape)
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # --- Simulation Loop ---
+ for k in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap = radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..4b9a9348ab4cae004fe6f5d81d87e0749069535c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/edit.diff
@@ -0,0 +1,222 @@
+--- a/original.py
++++ b/original.py
+@@ -1,186 +1,212 @@
+ # EVOLVE-BLOCK-START
+ """
+ Object-oriented physical simulation for n=26 circles.
+ This version refactors the simulation into a SimulationManager class,
+ encapsulating state, hyperparameters, and logic into a cohesive unit.
+ This structure improves clarity, maintainability, and facilitates the
+ implementation of complex, multi-phase optimization strategies.
+ """
+
+ import numpy as np
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for given center positions.
+ This is a stateless helper function using iterative proportional scaling.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # Initial radii are limited by distance to the walls of the unit square.
+ radii = np.minimum(radii, centers[:, 0])
+ radii = np.minimum(radii, 1 - centers[:, 0])
+ radii = np.minimum(radii, centers[:, 1])
+ radii = np.minimum(radii, 1 - centers[:, 1])
+
+ # Iteratively adjust radii based on distances between circle pairs.
+ # A high number of iterations ensures convergence to a stable state.
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Robustly handle coincident centers
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale down radii proportionally to resolve overlap
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+ class SimulationManager:
+ """
+ Manages the entire circle packing simulation process.
+ It encapsulates hyperparameters, state, and orchestrates the multi-phase optimization.
+ """
+ def __init__(self, n=26):
+ self.n = n
+ # Hyperparameters
+ self.main_iterations = 1000
+ self.main_learning_rate = 0.01
+ self.initial_growth_pressure = 1.015
+ self.final_growth_pressure = 1.0005
+
+- self.finetune_iterations = 250
+- self.finetune_learning_rate = 0.001
++ self.finetune_iterations = 300 # Increased for more precision
++ self.finetune_learning_rate = 0.0008 # Slower for fine adjustments
++ self.finetune_initial_growth_pressure = 1.0005 # Subtle growth pressure for fine-tuning
++ self.finetune_final_growth_pressure = 1.00001 # Almost neutral at end of fine-tune
++ self.initial_perturbation_scale = 0.005 # Small perturbation for initial grid
+
+ self.wall_repulsion_strength = 0.25
+
+ # State
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_circle_pos):
+- """Creates the initial 5x5 grid and places the 26th circle."""
++ """
++ Creates the initial 5x5 grid and places the 26th circle.
++ Applies a small random perturbation to the grid positions.
++ """
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_circle_pos
++
++ # Apply small random perturbation to initial 5x5 grid circles (first 25)
++ if self.initial_perturbation_scale > 0:
++ perturbation = np.random.normal(0, spacing * self.initial_perturbation_scale, size=(self.n, 2))
++ centers[:25, :] += perturbation[:25, :] # Apply to grid circles only
++ centers = np.clip(centers, 0.0, 1.0) # Ensure centers stay within bounds
++
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """Calculates repulsion forces using efficient vectorized operations."""
+ forces = np.zeros_like(centers)
+ inflated_radii = radii * growth_pressure_factor
+
+ # Vectorized circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ wall_forces = np.zeros_like(centers)
+ wall_forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 0])
+ wall_forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 1])
+ wall_forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 1] + inflated_radii) - 1.0)
+ forces += wall_forces
+
+ return forces
+
+ def _run_simulation_phase(self, centers, iterations, lr, initial_pressure, final_pressure):
+ """Runs a single phase of the simulation with annealed parameters."""
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers)
+
+ # Anneal growth pressure (linear decay)
+ pressure_factor = initial_pressure - (initial_pressure - final_pressure) * (sim_iter / iterations)
+
+ forces = self._calculate_forces(centers, radii, pressure_factor)
+
+ # Anneal learning rate (quadratic decay)
+ current_lr = lr * (1.0 - (sim_iter / iterations))**2
+
+ centers += forces * current_lr
+ centers = np.clip(centers, 0.0, 1.0)
+ return centers
+
+ def solve(self):
+ """Orchestrates the full optimization process and returns the best result."""
+ # Multi-start strategy: test different initial positions for the 26th circle.
+ candidate_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial
+ ]
+
+ for initial_pos in candidate_positions:
+ # --- Main Exploration Phase ---
+ initial_centers = self._initialize_centers(initial_pos)
+ main_phase_centers = self._run_simulation_phase(
+ initial_centers, self.main_iterations, self.main_learning_rate,
+ self.initial_growth_pressure, self.final_growth_pressure
+ )
+
+ # --- Fine-Tuning Phase ---
+ # This phase uses a small learning rate and no growth pressure for precision.
+ final_centers_trial = self._run_simulation_phase(
+ main_phase_centers, self.finetune_iterations, self.finetune_learning_rate,
+- 1.0, 1.0 # No growth pressure
++ self.finetune_initial_growth_pressure, self.finetune_final_growth_pressure
+ )
+
+ # Check if this trial yielded a better result
+ current_sum_radii = np.sum(compute_max_radii(final_centers_trial))
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = final_centers_trial.copy()
+
+ final_radii = compute_max_radii(self.best_final_centers)
+ return self.best_final_centers, final_radii
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by instantiating and running the SimulationManager.
+- This function acts as a clean entry point to the object-oriented simulation logic.
+- """
+- manager = SimulationManager(n=26)
++ This function acts as a clean entry point to the object-oriented simulation logic,
++ and now initializes the manager with carefully tuned hyperparameters.
++ """
++ manager = SimulationManager(
++ n=26,
++ main_iterations=1200, # Increased for more thorough initial exploration
++ main_learning_rate=0.01,
++ initial_growth_pressure=1.018, # Slightly higher initial pressure for stronger repulsion
++ final_growth_pressure=1.0001, # Closer to 1.0 for precise packing at end of main phase
++ finetune_iterations=300, # Increased fine-tune iterations for precision
++ finetune_learning_rate=0.0008, # Slower fine-tuning for high precision
++ finetune_initial_growth_pressure=1.0005, # Subtle growth pressure in fine-tune
++ finetune_final_growth_pressure=1.00001, # Almost zero growth pressure at the end of fine-tune
++ wall_repulsion_strength=0.3, # Slightly adjusted wall repulsion for better boundary interaction
++ initial_perturbation_scale=0.005 # Small random perturbation to initial grid (Recommendation 4)
++ )
+ centers, radii = manager.solve()
+ return centers, radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..10592c71695c1daa669c76a612a2bf015c92f475
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/main.py
@@ -0,0 +1,212 @@
+# EVOLVE-BLOCK-START
+"""
+Object-oriented physical simulation for n=26 circles.
+This version refactors the simulation into a SimulationManager class,
+encapsulating state, hyperparameters, and logic into a cohesive unit.
+This structure improves clarity, maintainability, and facilitates the
+implementation of complex, multi-phase optimization strategies.
+"""
+
+import numpy as np
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for given center positions.
+ This is a stateless helper function using iterative proportional scaling.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # Initial radii are limited by distance to the walls of the unit square.
+ radii = np.minimum(radii, centers[:, 0])
+ radii = np.minimum(radii, 1 - centers[:, 0])
+ radii = np.minimum(radii, centers[:, 1])
+ radii = np.minimum(radii, 1 - centers[:, 1])
+
+ # Iteratively adjust radii based on distances between circle pairs.
+ # A high number of iterations ensures convergence to a stable state.
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Robustly handle coincident centers
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale down radii proportionally to resolve overlap
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+class SimulationManager:
+ """
+ Manages the entire circle packing simulation process.
+ It encapsulates hyperparameters, state, and orchestrates the multi-phase optimization.
+ """
+ def __init__(self, n=26):
+ self.n = n
+ # Hyperparameters
+ self.main_iterations = 1000
+ self.main_learning_rate = 0.01
+ self.initial_growth_pressure = 1.015
+ self.final_growth_pressure = 1.0005
+
+ self.finetune_iterations = 300 # Increased for more precision
+ self.finetune_learning_rate = 0.0008 # Slower for fine adjustments
+ self.finetune_initial_growth_pressure = 1.0005 # Subtle growth pressure for fine-tuning
+ self.finetune_final_growth_pressure = 1.00001 # Almost neutral at end of fine-tune
+ self.initial_perturbation_scale = 0.005 # Small perturbation for initial grid
+
+ self.wall_repulsion_strength = 0.25
+
+ # State
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_circle_pos):
+ """
+ Creates the initial 5x5 grid and places the 26th circle.
+ Applies a small random perturbation to the grid positions.
+ """
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_circle_pos
+
+ # Apply small random perturbation to initial 5x5 grid circles (first 25)
+ if self.initial_perturbation_scale > 0:
+ perturbation = np.random.normal(0, spacing * self.initial_perturbation_scale, size=(self.n, 2))
+ centers[:25, :] += perturbation[:25, :] # Apply to grid circles only
+ centers = np.clip(centers, 0.0, 1.0) # Ensure centers stay within bounds
+
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """Calculates repulsion forces using efficient vectorized operations."""
+ forces = np.zeros_like(centers)
+ inflated_radii = radii * growth_pressure_factor
+
+ # Vectorized circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ wall_forces = np.zeros_like(centers)
+ wall_forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 0])
+ wall_forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 1])
+ wall_forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 1] + inflated_radii) - 1.0)
+ forces += wall_forces
+
+ return forces
+
+ def _run_simulation_phase(self, centers, iterations, lr, initial_pressure, final_pressure):
+ """Runs a single phase of the simulation with annealed parameters."""
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers)
+
+ # Anneal growth pressure (linear decay)
+ pressure_factor = initial_pressure - (initial_pressure - final_pressure) * (sim_iter / iterations)
+
+ forces = self._calculate_forces(centers, radii, pressure_factor)
+
+ # Anneal learning rate (quadratic decay)
+ current_lr = lr * (1.0 - (sim_iter / iterations))**2
+
+ centers += forces * current_lr
+ centers = np.clip(centers, 0.0, 1.0)
+ return centers
+
+ def solve(self):
+ """Orchestrates the full optimization process and returns the best result."""
+ # Multi-start strategy: test different initial positions for the 26th circle.
+ candidate_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial
+ ]
+
+ for initial_pos in candidate_positions:
+ # --- Main Exploration Phase ---
+ initial_centers = self._initialize_centers(initial_pos)
+ main_phase_centers = self._run_simulation_phase(
+ initial_centers, self.main_iterations, self.main_learning_rate,
+ self.initial_growth_pressure, self.final_growth_pressure
+ )
+
+ # --- Fine-Tuning Phase ---
+ # This phase uses a small learning rate and no growth pressure for precision.
+ final_centers_trial = self._run_simulation_phase(
+ main_phase_centers, self.finetune_iterations, self.finetune_learning_rate,
+ self.finetune_initial_growth_pressure, self.finetune_final_growth_pressure
+ )
+
+ # Check if this trial yielded a better result
+ current_sum_radii = np.sum(compute_max_radii(final_centers_trial))
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = final_centers_trial.copy()
+
+ final_radii = compute_max_radii(self.best_final_centers)
+ return self.best_final_centers, final_radii
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by instantiating and running the SimulationManager.
+ This function acts as a clean entry point to the object-oriented simulation logic,
+ and now initializes the manager with carefully tuned hyperparameters.
+ """
+ manager = SimulationManager(
+ n=26,
+ main_iterations=1200, # Increased for more thorough initial exploration
+ main_learning_rate=0.01,
+ initial_growth_pressure=1.018, # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure=1.0001, # Closer to 1.0 for precise packing at end of main phase
+ finetune_iterations=300, # Increased fine-tune iterations for precision
+ finetune_learning_rate=0.0008, # Slower fine-tuning for high precision
+ finetune_initial_growth_pressure=1.0005, # Subtle growth pressure in fine-tune
+ finetune_final_growth_pressure=1.00001, # Almost zero growth pressure at the end of fine-tune
+ wall_repulsion_strength=0.3, # Slightly adjusted wall repulsion for better boundary interaction
+ initial_perturbation_scale=0.005 # Small random perturbation to initial grid (Recommendation 4)
+ )
+ centers, radii = manager.solve()
+ return centers, radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c4ec3f080f6aff1249538b505bdab8e9deb64fe
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/original.py
@@ -0,0 +1,186 @@
+# EVOLVE-BLOCK-START
+"""
+Object-oriented physical simulation for n=26 circles.
+This version refactors the simulation into a SimulationManager class,
+encapsulating state, hyperparameters, and logic into a cohesive unit.
+This structure improves clarity, maintainability, and facilitates the
+implementation of complex, multi-phase optimization strategies.
+"""
+
+import numpy as np
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for given center positions.
+ This is a stateless helper function using iterative proportional scaling.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # Initial radii are limited by distance to the walls of the unit square.
+ radii = np.minimum(radii, centers[:, 0])
+ radii = np.minimum(radii, 1 - centers[:, 0])
+ radii = np.minimum(radii, centers[:, 1])
+ radii = np.minimum(radii, 1 - centers[:, 1])
+
+ # Iteratively adjust radii based on distances between circle pairs.
+ # A high number of iterations ensures convergence to a stable state.
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Robustly handle coincident centers
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale down radii proportionally to resolve overlap
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+class SimulationManager:
+ """
+ Manages the entire circle packing simulation process.
+ It encapsulates hyperparameters, state, and orchestrates the multi-phase optimization.
+ """
+ def __init__(self, n=26):
+ self.n = n
+ # Hyperparameters
+ self.main_iterations = 1000
+ self.main_learning_rate = 0.01
+ self.initial_growth_pressure = 1.015
+ self.final_growth_pressure = 1.0005
+
+ self.finetune_iterations = 250
+ self.finetune_learning_rate = 0.001
+
+ self.wall_repulsion_strength = 0.25
+
+ # State
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_circle_pos):
+ """Creates the initial 5x5 grid and places the 26th circle."""
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_circle_pos
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """Calculates repulsion forces using efficient vectorized operations."""
+ forces = np.zeros_like(centers)
+ inflated_radii = radii * growth_pressure_factor
+
+ # Vectorized circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ wall_forces = np.zeros_like(centers)
+ wall_forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 0])
+ wall_forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, inflated_radii - centers[:, 1])
+ wall_forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, (centers[:, 1] + inflated_radii) - 1.0)
+ forces += wall_forces
+
+ return forces
+
+ def _run_simulation_phase(self, centers, iterations, lr, initial_pressure, final_pressure):
+ """Runs a single phase of the simulation with annealed parameters."""
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers)
+
+ # Anneal growth pressure (linear decay)
+ pressure_factor = initial_pressure - (initial_pressure - final_pressure) * (sim_iter / iterations)
+
+ forces = self._calculate_forces(centers, radii, pressure_factor)
+
+ # Anneal learning rate (quadratic decay)
+ current_lr = lr * (1.0 - (sim_iter / iterations))**2
+
+ centers += forces * current_lr
+ centers = np.clip(centers, 0.0, 1.0)
+ return centers
+
+ def solve(self):
+ """Orchestrates the full optimization process and returns the best result."""
+ # Multi-start strategy: test different initial positions for the 26th circle.
+ candidate_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial
+ ]
+
+ for initial_pos in candidate_positions:
+ # --- Main Exploration Phase ---
+ initial_centers = self._initialize_centers(initial_pos)
+ main_phase_centers = self._run_simulation_phase(
+ initial_centers, self.main_iterations, self.main_learning_rate,
+ self.initial_growth_pressure, self.final_growth_pressure
+ )
+
+ # --- Fine-Tuning Phase ---
+ # This phase uses a small learning rate and no growth pressure for precision.
+ final_centers_trial = self._run_simulation_phase(
+ main_phase_centers, self.finetune_iterations, self.finetune_learning_rate,
+ 1.0, 1.0 # No growth pressure
+ )
+
+ # Check if this trial yielded a better result
+ current_sum_radii = np.sum(compute_max_radii(final_centers_trial))
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = final_centers_trial.copy()
+
+ final_radii = compute_max_radii(self.best_final_centers)
+ return self.best_final_centers, final_radii
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by instantiating and running the SimulationManager.
+ This function acts as a clean entry point to the object-oriented simulation logic.
+ """
+ manager = SimulationManager(n=26)
+ centers, radii = manager.solve()
+ return centers, radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e6bb5502ca5abe10fd560e6611e43f97ea21718e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_50/search_replace.txt
@@ -0,0 +1,142 @@
+
+manager_params_and_perturbation
+
+
+
+This edit enhances the `SimulationManager` class by adding parameters for initial grid perturbation and annealed growth pressure during the fine-tuning phase.
+
+1. **`__init__` Update:** New parameters `initial_perturbation_scale`, `finetune_initial_growth_pressure`, and `finetune_final_growth_pressure` are added to the constructor. Default values for `finetune_iterations` and `finetune_learning_rate` are updated to match previous high-scoring configurations.
+2. **`_initialize_centers` Enhancement:** The method now applies a small random perturbation to the initial 5x5 grid circle positions, controlled by `initial_perturbation_scale`. This helps to break perfect symmetry and avoid getting stuck in suboptimal grid-like local minima (Recommendation 4).
+3. **`solve` Fine-Tuning Integration:** The call to `_run_simulation_phase` for the fine-tuning stage now utilizes the newly added `finetune_initial_growth_pressure` and `finetune_final_growth_pressure` parameters, enabling a subtle annealing of growth pressure during the final adjustments. This is consistent with successful prior implementations (Recommendation 2) and aims for more precise, tighter packing.
+
+
+
+<<<<<<< SEARCH
+ self.finetune_iterations = 250
+ self.finetune_learning_rate = 0.001
+
+ self.wall_repulsion_strength = 0.25
+
+ # State
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_circle_pos):
+ """Creates the initial 5x5 grid and places the 26th circle."""
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_circle_pos
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """Calculates repulsion forces using efficient vectorized operations."""
+=======
+ self.finetune_iterations = 300 # Increased for more precision
+ self.finetune_learning_rate = 0.0008 # Slower for fine adjustments
+ self.finetune_initial_growth_pressure = 1.0005 # Subtle growth pressure for fine-tuning
+ self.finetune_final_growth_pressure = 1.00001 # Almost neutral at end of fine-tune
+ self.initial_perturbation_scale = 0.005 # Small perturbation for initial grid
+
+ self.wall_repulsion_strength = 0.25
+
+ # State
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_circle_pos):
+ """
+ Creates the initial 5x5 grid and places the 26th circle.
+ Applies a small random perturbation to the grid positions.
+ """
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ centers[25] = extra_circle_pos
+
+ # Apply small random perturbation to initial 5x5 grid circles (first 25)
+ if self.initial_perturbation_scale > 0:
+ perturbation = np.random.normal(0, spacing * self.initial_perturbation_scale, size=(self.n, 2))
+ centers[:25, :] += perturbation[:25, :] # Apply to grid circles only
+ centers = np.clip(centers, 0.0, 1.0) # Ensure centers stay within bounds
+
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """Calculates repulsion forces using efficient vectorized operations."""
+>>>>>>> REPLACE
+<<<<<<< SEARCH
+ final_centers_trial = self._run_simulation_phase(
+ main_phase_centers, self.finetune_iterations, self.finetune_learning_rate,
+ 1.0, 1.0 # No growth pressure
+ )
+=======
+ final_centers_trial = self._run_simulation_phase(
+ main_phase_centers, self.finetune_iterations, self.finetune_learning_rate,
+ self.finetune_initial_growth_pressure, self.finetune_final_growth_pressure
+ )
+>>>>>>> REPLACE
+
+
+
+tune_simulation_manager_params
+
+
+
+This edit explicitly instantiates the `SimulationManager` in `construct_packing` with a set of carefully tuned hyperparameters. These parameters are derived from previous high-scoring versions and aim to:
+
+1. **Increase Main Simulation Iterations:** `main_iterations=1200` for more thorough initial exploration.
+2. **Adjust Growth Pressures:** `initial_growth_pressure=1.018` and `final_growth_pressure=1.0001` are slightly more aggressive to encourage tighter packing while still allowing the system to settle.
+3. **Refine Wall Repulsion:** `wall_repulsion_strength=0.3` for a slightly firmer interaction with boundaries, preventing circles from drifting too far from edges.
+4. **Leverage Fine-Tuning:** The fine-tuning parameters (`finetune_iterations`, `finetune_learning_rate`, `finetune_initial_growth_pressure`, `finetune_final_growth_pressure`) are now effectively used and tuned for precision after the main exploration phase.
+5. **Enable Initial Perturbation:** `initial_perturbation_scale=0.005` is explicitly set to introduce slight randomness in the initial 5x5 grid, helping to escape symmetrical local minima.
+
+By explicitly setting these parameters, the simulation is guided towards a better global optimum, leveraging successful configurations from prior program generations.
+
+
+
+<<<<<<< SEARCH
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by instantiating and running the SimulationManager.
+ This function acts as a clean entry point to the object-oriented simulation logic.
+ """
+ manager = SimulationManager(n=26)
+ centers, radii = manager.solve()
+ return centers, radii
+=======
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by instantiating and running the SimulationManager.
+ This function acts as a clean entry point to the object-oriented simulation logic,
+ and now initializes the manager with carefully tuned hyperparameters.
+ """
+ manager = SimulationManager(
+ n=26,
+ main_iterations=1200, # Increased for more thorough initial exploration
+ main_learning_rate=0.01,
+ initial_growth_pressure=1.018, # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure=1.0001, # Closer to 1.0 for precise packing at end of main phase
+ finetune_iterations=300, # Increased fine-tune iterations for precision
+ finetune_learning_rate=0.0008, # Slower fine-tuning for high precision
+ finetune_initial_growth_pressure=1.0005, # Subtle growth pressure in fine-tune
+ finetune_final_growth_pressure=1.00001, # Almost zero growth pressure at the end of fine-tune
+ wall_repulsion_strength=0.3, # Slightly adjusted wall repulsion for better boundary interaction
+ initial_perturbation_scale=0.005 # Small random perturbation to initial grid (Recommendation 4)
+ )
+ centers, radii = manager.solve()
+ return centers, radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..b76378960f2346eb70c9d4c961ea3626b9a03819
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/edit.diff
@@ -0,0 +1,231 @@
+--- a/original.py
++++ b/original.py
+@@ -1,218 +1,219 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ This version is a crossover, combining the high-level strategy of the best-performing
+ parent (multi-start, two-phase simulation, annealed growth pressure) with the
+ efficient vectorized force calculations from the inspiration program.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- # Tuned hyperparameters based on successful prior implementations
+- iterations = 1200 # Increased for more thorough exploration
+- learning_rate = 0.01
+- wall_repulsion_strength = 0.3 # Adjusted wall repulsion for better boundary interaction
+- initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
++ # --- Hyperparameters for the simulation ---
++ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
++ iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
++ learning_rate = 0.015 # Increased learning rate for more significant center movements
++ wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
++ initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+- perturbation_scale = 0.002 # A small fraction of the spacing
++ perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+- # Anneal growth pressure quadratically (Recommendation 2 from previous G37)
++ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 300 # Increased fine-tune iterations for precision
+- fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
++ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
++ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ccd51bfa28a6cc632433ce980e1b59b6ad0e2cb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/main.py
@@ -0,0 +1,219 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+ iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
+ learning_rate = 0.015 # Increased learning rate for more significant center movements
+ wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
+ initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..15e2065d10b82e1a24064c8969333788774107fb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/original.py
@@ -0,0 +1,218 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters based on successful prior implementations
+ iterations = 1200 # Increased for more thorough exploration
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.3 # Adjusted wall repulsion for better boundary interaction
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically (Recommendation 2 from previous G37)
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 300 # Increased fine-tune iterations for precision
+ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c9a6dc5b766e61b941dec976c303fc47fe6cdb43
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_51/search_replace.txt
@@ -0,0 +1,217 @@
+
+aggressive_explosion_params
+
+
+
+This edit aims to enhance the exploration capability of the main simulation phase by increasing the initial repulsive forces and allowing more time for the circles to settle. The current visualization shows a somewhat rigid grid structure with many small circles, suggesting the simulation might be getting stuck in local minima.
+
+1. **Increased `initial_growth_pressure` (1.018 -> 1.03):** A higher initial growth pressure will cause circles to repel each other more strongly at the beginning of the simulation. This "explosion" is intended to break the initial 5x5 grid symmetry more effectively, allowing the circles to explore a wider range of configurations and potentially escape local minima.
+2. **Increased `learning_rate` (0.01 -> 0.015):** A higher learning rate in the main phase allows centers to move more significantly in response to forces, facilitating larger reconfigurations, especially during the initial aggressive repulsion.
+3. **Increased `iterations` (1200 -> 1500):** With stronger initial forces and a higher learning rate, more iterations are needed for the system to converge and settle into a stable configuration.
+4. **Increased `wall_repulsion_strength` (0.3 -> 0.35):** Slightly stronger wall repulsion will help contain the circles during the more aggressive "explosion" phase, preventing them from being pushed too far outside the unit square boundaries while maintaining overall pressure.
+5. **Increased `perturbation_scale` (0.002 -> 0.005):** A larger initial random perturbation to the 5x5 grid aims to further disrupt the initial symmetry from the very start, giving the simulation a less biased starting point for exploration.
+6. **Increased `fine_tune_iterations` (300 -> 400) and `fine_tune_learning_rate` (0.0008 -> 0.001):** After a more aggressive main phase, the resulting configuration might be further from a perfectly settled state. Increased fine-tuning iterations and a slightly higher learning rate will provide more time and capacity for the system to precisely converge in the final phase.
+
+
+
+<<<<<<< SEARCH
+ # Tuned hyperparameters based on successful prior implementations
+ iterations = 1200 # Increased for more thorough exploration
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.3 # Adjusted wall repulsion for better boundary interaction
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically (Recommendation 2 from previous G37)
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 300 # Increased fine-tune iterations for precision
+ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+=======
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+ iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
+ learning_rate = 0.015 # Increased learning rate for more significant center movements
+ wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
+ initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0e6a6df4216e19f54a9a02e3efc30d45b4ae691c
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..9f1337152b410a560340df1173c96a185c97084a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/edit.diff
@@ -0,0 +1,248 @@
+--- a/original.py
++++ b/original.py
+@@ -1,199 +1,219 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ This version is a crossover, combining the high-level strategy of the best-performing
+ parent (multi-start, two-phase simulation, annealed growth pressure) with the
+-efficient vectorized force calculations from the inspiration program.
++efficient vectorized force calculations from the inspiration program, plus
++reintroducing initial perturbation for better exploration.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- # Increased iterations due to vectorized efficiency
+- iterations = 750
++ # Tuned hyperparameters based on successful prior implementations and crossovers
++ iterations = 1200 # Increased for more thorough exploration, from Inspiration A
+ learning_rate = 0.01
+- wall_repulsion_strength = 0.5
+- initial_growth_pressure = 1.01
+- final_growth_pressure = 1.001
++ wall_repulsion_strength = 0.35 # Slightly softer walls than 0.5, but firmer than 0.25 in G37 for better boundary utilization
++ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion (from Inspiration A)
++ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase (from Inspiration A)
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
++
++ # Add small random perturbation to the 5x5 grid (first 25 circles, re-introduced from successful prior)
++ perturbation_scale = 0.002 # A small fraction of the spacing
++ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
++
+ current_centers[25] = initial_pos_26th_circle
++
++ # Ensure all centers stay within bounds after initial setup and perturbation
++ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+-
+- # Anneal growth pressure
+- current_growth_pressure = initial_growth_pressure - \
+- (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
++
++ # Anneal growth pressure quadratically (from Inspiration A)
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * \
++ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+-
++
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+-
++
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+-
++
+ # --- Update center positions ---
+- current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
++ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2 # Quadratic annealing for LR
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 100 # Increased due to efficiency
+- fine_tune_learning_rate = 0.001
++ fine_tune_iterations = 300 # Increased fine-tune iterations for precision (from Inspiration A)
++ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision (from Inspiration A)
++ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (from Inspiration A)
++ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (from Inspiration A)
+ centers_for_sim = best_final_centers.copy()
+
+- for _ in range(fine_tune_iterations):
++ for sim_iter_ft in range(fine_tune_iterations): # Using sim_iter_ft for annealing (from Inspiration A)
+ radii = compute_max_radii(centers_for_sim)
+
+- # --- Vectorized Force Calculation (no growth pressure) ---
++ # Anneal subtle growth pressure (linear decay for stability, from Inspiration A)
++ current_finetune_growth_pressure = finetune_growth_pressure_start - \
++ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
++ (sim_iter_ft / fine_tune_iterations)
++ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
++
++ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
++ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+-
++
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+- # b. Wall forces
++ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+- overlap_bottom = radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
++ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
++ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+- centers_for_sim += forces * fine_tune_learning_rate
++ # Anneal learning rate for fine-tuning (linear decay, from Inspiration A)
++ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
++ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..da12e3738db01dbf266007c51f7158d5ec3fef20
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/main.py
@@ -0,0 +1,219 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program, plus
+reintroducing initial perturbation for better exploration.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters based on successful prior implementations and crossovers
+ iterations = 1200 # Increased for more thorough exploration, from Inspiration A
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.35 # Slightly softer walls than 0.5, but firmer than 0.25 in G37 for better boundary utilization
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion (from Inspiration A)
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase (from Inspiration A)
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, re-introduced from successful prior)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically (from Inspiration A)
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2 # Quadratic annealing for LR
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 300 # Increased fine-tune iterations for precision (from Inspiration A)
+ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision (from Inspiration A)
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (from Inspiration A)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (from Inspiration A)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Using sim_iter_ft for annealing (from Inspiration A)
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability, from Inspiration A)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay, from Inspiration A)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..648682d4ca9de4b97c1ed5073dbaed558d912fc7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/original.py
@@ -0,0 +1,199 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..d443e3c92f2e45b13b8498248143ce87d9f250a8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results
+Run 1/1 completed in 24.49 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9365786210252534
+ public: {'centers_str': ' centers[0] = (0.0999, 0.0999)\n centers[1] = (0.1001, 0.3000)\n centers[2] = (0.1000, 0.5003)\n centers[3] = (0.1000, 0.6994)\n centers[4] = (0.1008, 0.8990)\n centers[5] = (0.3050, 0.0957)\n centers[6] = (0.2998, 0.3002)\n centers[7] = (0.3001, 0.5003)\n centers[8] = (0.3003, 0.6999)\n centers[9] = (0.3000, 0.9003)\n centers[10] = (0.4994, 0.0998)\n centers[11] = (0.4997, 0.2996)\n centers[12] = (0.4996, 0.4993)\n centers[13] = (0.5002, 0.6995)\n centers[14] = (0.4998, 0.8999)\n centers[15] = (0.6999, 0.1008)\n centers[16] = (0.7001, 0.2998)\n centers[17] = (0.6998, 0.4942)\n centers[18] = (0.6946, 0.6983)\n centers[19] = (0.7005, 0.9058)\n centers[20] = (0.8999, 0.0980)\n centers[21] = (0.8985, 0.2983)\n centers[22] = (0.8983, 0.5026)\n centers[23] = (0.9042, 0.7015)\n centers[24] = (0.8993, 0.8994)\n centers[25] = (0.1954, 0.2045)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9365786210252534}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/packing_viz.png
+ execution_time_mean: 24.491457510273904
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..07330405b2345a53faad4583af20c3fff75238c8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9365786210252534,
+ "public": {
+ "centers_str": " centers[0] = (0.0999, 0.0999)\n centers[1] = (0.1001, 0.3000)\n centers[2] = (0.1000, 0.5003)\n centers[3] = (0.1000, 0.6994)\n centers[4] = (0.1008, 0.8990)\n centers[5] = (0.3050, 0.0957)\n centers[6] = (0.2998, 0.3002)\n centers[7] = (0.3001, 0.5003)\n centers[8] = (0.3003, 0.6999)\n centers[9] = (0.3000, 0.9003)\n centers[10] = (0.4994, 0.0998)\n centers[11] = (0.4997, 0.2996)\n centers[12] = (0.4996, 0.4993)\n centers[13] = (0.5002, 0.6995)\n centers[14] = (0.4998, 0.8999)\n centers[15] = (0.6999, 0.1008)\n centers[16] = (0.7001, 0.2998)\n centers[17] = (0.6998, 0.4942)\n centers[18] = (0.6946, 0.6983)\n centers[19] = (0.7005, 0.9058)\n centers[20] = (0.8999, 0.0980)\n centers[21] = (0.8985, 0.2983)\n centers[22] = (0.8983, 0.5026)\n centers[23] = (0.9042, 0.7015)\n centers[24] = (0.8993, 0.8994)\n centers[25] = (0.1954, 0.2045)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9365786210252534
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/results/packing_viz.png",
+ "execution_time_mean": 24.491457510273904,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6a1c05fa01291460d83b41a63f841eba805cf006
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_52/rewrite.txt
@@ -0,0 +1,210 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program, plus
+reintroducing initial perturbation for better exploration.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters based on successful prior implementations and crossovers
+ iterations = 1200 # Increased for more thorough exploration, from Inspiration A
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.35 # Slightly softer walls than 0.5, but firmer than 0.25 in G37 for better boundary utilization
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion (from Inspiration A)
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase (from Inspiration A)
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, re-introduced from successful prior)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically (from Inspiration A)
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2 # Quadratic annealing for LR
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 300 # Increased fine-tune iterations for precision (from Inspiration A)
+ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision (from Inspiration A)
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (from Inspiration A)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (from Inspiration A)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Using sim_iter_ft for annealing (from Inspiration A)
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability, from Inspiration A)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay, from Inspiration A)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..ac902f3836c4bb9748aff6a97c12de17a40c8fee
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/edit.diff
@@ -0,0 +1,229 @@
+--- a/original.py
++++ b/original.py
+@@ -1,219 +1,219 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ This version is a crossover, combining the high-level strategy of the best-performing
+ parent (multi-start, two-phase simulation, annealed growth pressure) with the
+ efficient vectorized force calculations from the inspiration program.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+- iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
+- learning_rate = 0.015 # Increased learning rate for more significant center movements
+- wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
+- initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
++ iterations = 2000 # Increased iterations for more thorough exploration after stronger forces
++ learning_rate = 0.02 # Increased learning rate for more significant center movements
++ wall_repulsion_strength = 0.3 # Slightly reduced wall repulsion to allow more freedom of movement initially
++ initial_growth_pressure = 1.05 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+- perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
++ perturbation_scale = 0.01 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+- # Anneal growth pressure quadratically
++ # Anneal growth pressure linearly for more sustained high pressure
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+- (1.0 - (sim_iter / iterations))**2
++ (1.0 - (sim_iter / iterations))
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..960a167e3699c8813d26ae93f0e69a32c8aa80ca
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/main.py
@@ -0,0 +1,219 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+ iterations = 2000 # Increased iterations for more thorough exploration after stronger forces
+ learning_rate = 0.02 # Increased learning rate for more significant center movements
+ wall_repulsion_strength = 0.3 # Slightly reduced wall repulsion to allow more freedom of movement initially
+ initial_growth_pressure = 1.05 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.01 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure linearly for more sustained high pressure
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ccd51bfa28a6cc632433ce980e1b59b6ad0e2cb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/original.py
@@ -0,0 +1,219 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+ iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
+ learning_rate = 0.015 # Increased learning rate for more significant center movements
+ wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
+ initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..73435f59b2e2a9544d5f1a20f40983e71681ec7f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_53/search_replace.txt
@@ -0,0 +1,119 @@
+
+aggressive_main_phase_and_linear_gp_annealing
+
+
+
+The previous results show a sum of radii of 1.95, which indicates the simulation might be getting stuck in local optima, likely due to the initial 5x5 grid pattern. The current visualization suggests a somewhat symmetric arrangement, which is often not optimal for 26 circles. To break this symmetry more effectively and explore the solution space more thoroughly, I propose the following adjustments:
+
+1. **More Aggressive Main Phase Parameters:**
+ * **Increase `iterations` to 2000:** A longer main simulation phase provides more time for the forces to act and for circles to settle into better positions, especially after more aggressive initial forces.
+ * **Increase `learning_rate` to 0.02:** A slightly higher learning rate allows for more significant movement of circle centers in each step, aiding in escaping local minima.
+ * **Increase `initial_growth_pressure` to 1.05:** A significantly higher initial growth pressure will cause a stronger "explosion" or repulsion among circles, forcing them to spread out more forcefully and break initial grid structures.
+ * **Increase `perturbation_scale` to 0.01:** A larger initial random perturbation on the 25 grid circles will further help in breaking the inherent symmetry of the starting configuration, pushing the system towards less conventional but potentially more optimal arrangements.
+ * **Decrease `wall_repulsion_strength` to 0.3**: A slightly softer wall repulsion during the main exploration phase gives circles more freedom to move and find their optimal positions, without being too constrained by the boundaries early on. The fine-tuning phase can then tighten them against the walls.
+
+2. **Linear Annealing for Main Phase Growth Pressure:**
+ * Change the growth pressure annealing schedule in the main phase from quadratic (`**2`) to linear (`**1`). The quadratic decay causes the pressure to drop relatively quickly at the beginning of the simulation. By making it linear, the high growth pressure will be sustained for a longer portion of the main phase. This prolonged aggressive repulsion is intended to keep the system in a more "fluid" and exploratory state for a longer duration, helping to overcome initial local optima before it starts settling.
+
+These changes are designed to collectively promote a more chaotic and exploratory main simulation phase, increasing the chances of finding a better initial configuration for the fine-tuning phase and ultimately a higher sum of radii.
+
+
+
+<<<<<<< SEARCH
+ iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
+ learning_rate = 0.015 # Increased learning rate for more significant center movements
+ wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
+ initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+=======
+ iterations = 2000 # Increased iterations for more thorough exploration after stronger forces
+ learning_rate = 0.02 # Increased learning rate for more significant center movements
+ wall_repulsion_strength = 0.3 # Slightly reduced wall repulsion to allow more freedom of movement initially
+ initial_growth_pressure = 1.05 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.01 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure linearly for more sustained high pressure
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))
+ inflated_radii = radii * current_growth_pressure
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8735232c1779f939f63799a4e1d5f18aa16b4427
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..78464be4a2b6209195642016f64cbd81fc5052a8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/edit.diff
@@ -0,0 +1,330 @@
+--- a/original.py
++++ b/original.py
+@@ -1,256 +1,304 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+- # Tuned based on high-scoring prior programs.
+- simulation_iterations = 500 # Number of steps for force-directed simulation
+- initial_learning_rate = 0.02 # A more stable learning rate for refinement.
+- wall_repulsion_strength = 0.8 # Stronger walls to utilize boundary space.
+- growth_pressure = 1.005 # Key parameter to create expansion force.
++ # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
++ main_iterations = 1200 # Number of steps for main force-directed simulation
++ main_learning_rate = 0.01 # Initial learning rate for main phase
++ wall_repulsion_strength = 0.35 # Balanced wall repulsion to utilize boundaries effectively
++ initial_growth_pressure = 1.018 # Stronger initial growth pressure for aggressive exploration
++ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
++
++ fine_tune_iterations = 300 # Number of steps for fine-tuning
++ fine_tune_learning_rate = 0.0008 # Very low learning rate for precise adjustments
++ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure for fine-tuning
++ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+- # --- Physical Simulation Loop (Force-Directed Relaxation) ---
++ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+- for sim_iter in range(simulation_iterations):
+- # 1. Calculate radii for the current positions.
++ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+- # 1.5. Artificially inflate radii to create an expansion 'pressure'.
+- # This is the crucial step that drives the simulation.
+- pressured_radii = radii_for_sim * growth_pressure
+-
+- # 2. Calculate forces to push centers to a better configuration
++ # Anneal growth pressure quadratically
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * \
++ (1.0 - (sim_iter / main_iterations))**2
++ pressured_radii = radii_for_sim * current_growth_pressure
++
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+- # Calculate overlap based on the artificially inflated radii.
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+- # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+- # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+- current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
++ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+-
+- # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+- # After simulation, use the refined centers to compute FINAL, accurate radii
++ # --- Post-Simulation Fine-Tuning Phase ---
++ # Continue from the best state of the main simulation for precision.
++ for fine_tune_iter in range(fine_tune_iterations):
++ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
++
++ # Anneal subtle growth pressure (linear decay)
++ current_finetune_growth_pressure = finetune_growth_pressure_start - \
++ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
++ (fine_tune_iter / fine_tune_iterations)
++ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
++
++ forces_ft = np.zeros((n, 2))
++
++ # a. Circle-to-circle repulsion forces
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers_for_sim[i] - centers_for_sim[j]
++ dist = np.linalg.norm(vec)
++
++ if dist < 1e-9:
++ continue
++
++ overlap = pressured_radii_ft[i] + pressured_radii_ft[j] - dist
++
++ if overlap > 0:
++ force_magnitude = overlap
++ force_vec = (vec / dist) * force_magnitude
++ forces_ft[i] += force_vec
++ forces_ft[j] -= force_vec
++
++ # b. Wall repulsion forces
++ for i in range(n):
++ overlap_left = pressured_radii_ft[i] - centers_for_sim[i, 0]
++ if overlap_left > 0:
++ forces_ft[i, 0] += wall_repulsion_strength * overlap_left
++ overlap_right = (centers_for_sim[i, 0] + pressured_radii_ft[i]) - 1.0
++ if overlap_right > 0:
++ forces_ft[i, 0] -= wall_repulsion_strength * overlap_right
++ overlap_bottom = pressured_radii_ft[i] - centers_for_sim[i, 1]
++ if overlap_bottom > 0:
++ forces_ft[i, 1] += wall_repulsion_strength * overlap_bottom
++ overlap_top = (centers_for_sim[i, 1] + pressured_radii_ft[i]) - 1.0
++ if overlap_top > 0:
++ forces_ft[i, 1] -= wall_repulsion_strength * overlap_top
++
++ # Update center positions
++ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
++ centers_for_sim += forces_ft * current_fine_tune_lr
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+ def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..392c1830f2d91ecbf79feef1962c630f2f64903c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/main.py
@@ -0,0 +1,304 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+ main_iterations = 1200 # Number of steps for main force-directed simulation
+ main_learning_rate = 0.01 # Initial learning rate for main phase
+ wall_repulsion_strength = 0.35 # Balanced wall repulsion to utilize boundaries effectively
+ initial_growth_pressure = 1.018 # Stronger initial growth pressure for aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+ fine_tune_iterations = 300 # Number of steps for fine-tuning
+ fine_tune_learning_rate = 0.0008 # Very low learning rate for precise adjustments
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure for fine-tuning
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ overlap = pressured_radii_ft[i] + pressured_radii_ft[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces_ft[i] += force_vec
+ forces_ft[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ overlap_left = pressured_radii_ft[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces_ft[i, 0] += wall_repulsion_strength * overlap_left
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii_ft[i]) - 1.0
+ if overlap_right > 0:
+ forces_ft[i, 0] -= wall_repulsion_strength * overlap_right
+ overlap_bottom = pressured_radii_ft[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces_ft[i, 1] += wall_repulsion_strength * overlap_bottom
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii_ft[i]) - 1.0
+ if overlap_top > 0:
+ forces_ft[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b901cf614ca947e5bc73a16b33ef8ab284cd09e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/original.py
@@ -0,0 +1,256 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+ initial_learning_rate = 0.02 # A more stable learning rate for refinement.
+ wall_repulsion_strength = 0.8 # Stronger walls to utilize boundary space.
+ growth_pressure = 1.005 # Key parameter to create expansion force.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 1.5. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the crucial step that drives the simulation.
+ pressured_radii = radii_for_sim * growth_pressure
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ # Calculate overlap based on the artificially inflated radii.
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..99037384d5cad60d2af9aa48cf36aa72ed527703
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results
+Run 1/1 completed in 435.51 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9364314218252572
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8994, 0.1006)\n centers[5] = (0.0955, 0.3045)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.4945, 0.7001)\n centers[18] = (0.6986, 0.6944)\n centers[19] = (0.9060, 0.7001)\n centers[20] = (0.0981, 0.8998)\n centers[21] = (0.2985, 0.8985)\n centers[22] = (0.5026, 0.8984)\n centers[23] = (0.7014, 0.9041)\n centers[24] = (0.8993, 0.8991)\n centers[25] = (0.2045, 0.1955)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9364314218252572}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/packing_viz.png
+ execution_time_mean: 435.5141034880653
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..37126c7ab65d9697661a9515771a2e7743fb826a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9364314218252572,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3000, 0.1000)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8994, 0.1006)\n centers[5] = (0.0955, 0.3045)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.4945, 0.7001)\n centers[18] = (0.6986, 0.6944)\n centers[19] = (0.9060, 0.7001)\n centers[20] = (0.0981, 0.8998)\n centers[21] = (0.2985, 0.8985)\n centers[22] = (0.5026, 0.8984)\n centers[23] = (0.7014, 0.9041)\n centers[24] = (0.8993, 0.8991)\n centers[25] = (0.2045, 0.1955)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9364314218252572
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/results/packing_viz.png",
+ "execution_time_mean": 435.5141034880653,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c9576f6cf00757fdd62b3a6362df99bf6801a367
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_54/search_replace.txt
@@ -0,0 +1,253 @@
+The current implementation of the circle packing algorithm uses a single simulation phase with a constant growth pressure. Drawing insights from higher-scoring previous programs, a two-phase simulation approach with annealed growth pressure and learning rates has proven more effective. This allows for aggressive exploration in an initial phase and precise settling in a subsequent fine-tuning phase.
+
+My proposed improvements involve:
+1. **Introduce annealed growth pressure in the main simulation loop:** Instead of a constant `growth_pressure`, I will introduce `initial_growth_pressure` and `final_growth_pressure` and apply a quadratic annealing schedule. This allows the simulation to start with a stronger "explosion" force and gradually reduce it to enable a denser pack. I will also adjust general hyperparameters like iterations, learning rate, and wall repulsion to match configurations that yielded higher scores previously.
+2. **Add a dedicated fine-tuning phase:** After the main simulation, a second phase will run with a much lower learning rate and very subtle, annealed growth pressure. This phase is designed to precisely adjust the circle positions and maximize their radii without disrupting the overall arrangement found in the main exploration phase.
+
+These changes are aimed at improving the efficiency of the exploration-exploitation balance in the simulation, leading to a higher sum of radii.
+
+
+annealed_growth_and_two_phase_sim
+
+
+
+This edit refactors the simulation hyperparameters and introduces a two-phase simulation strategy. The first phase (main simulation) now uses annealed growth pressure and learning rates, allowing for dynamic exploration. A second, dedicated fine-tuning phase is added to precisely settle the circles with a subtle, annealed growth pressure and very low learning rates, enhancing the final packing density. This mirrors strategies from higher-performing prior programs.
+
+
+
+<<<<<<< SEARCH
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 500 # Number of steps for force-directed simulation
+ initial_learning_rate = 0.02 # A more stable learning rate for refinement.
+ wall_repulsion_strength = 0.8 # Stronger walls to utilize boundary space.
+ growth_pressure = 1.005 # Key parameter to create expansion force.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 1.5. Artificially inflate radii to create an expansion 'pressure'.
+ # This is the crucial step that drives the simulation.
+ pressured_radii = radii_for_sim * growth_pressure
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ # Calculate overlap based on the artificially inflated radii.
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ # Force magnitude proportional to overlap
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After simulation, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+=======
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+ main_iterations = 1200 # Number of steps for main force-directed simulation
+ main_learning_rate = 0.01 # Initial learning rate for main phase
+ wall_repulsion_strength = 0.35 # Balanced wall repulsion to utilize boundaries effectively
+ initial_growth_pressure = 1.018 # Stronger initial growth pressure for aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+ fine_tune_iterations = 300 # Number of steps for fine-tuning
+ fine_tune_learning_rate = 0.0008 # Very low learning rate for precise adjustments
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure for fine-tuning
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ overlap = pressured_radii_ft[i] + pressured_radii_ft[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces_ft[i] += force_vec
+ forces_ft[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ overlap_left = pressured_radii_ft[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces_ft[i, 0] += wall_repulsion_strength * overlap_left
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii_ft[i]) - 1.0
+ if overlap_right > 0:
+ forces_ft[i, 0] -= wall_repulsion_strength * overlap_right
+ overlap_bottom = pressured_radii_ft[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces_ft[i, 1] += wall_repulsion_strength * overlap_bottom
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii_ft[i]) - 1.0
+ if overlap_top > 0:
+ forces_ft[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a7569e3af1becbe5942f5c3f01ff461acf20ddfa
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..3c45c7c91e1cf3fb0bb264885971d824298b2269
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/edit.diff
@@ -0,0 +1,363 @@
+--- a/original.py
++++ b/original.py
+@@ -1,210 +1,197 @@
+ # EVOLVE-BLOCK-START
+-"""
+-Physical simulation-based circle packing for n=26 circles.
+-This algorithm is a crossover, combining the high-level strategy of the best-performing
+-parent (multi-start, two-phase simulation, annealed growth pressure) with the
+-efficient vectorized force calculations from the inspiration program.
+-"""
+-
+ import numpy as np
+-
+-def construct_packing():
+- """
+- Constructs a packing of 26 circles by simulating physical repulsion forces
+- to find an optimal arrangement of centers.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- n = 26
+-
+- # --- Hyperparameters for the simulation ---
+- # Tuned hyperparameters based on successful class-based implementation (G37 equivalent)
+- iterations = 1200 # Increased for more thorough exploration
+- learning_rate = 0.01
+- wall_repulsion_strength = 0.3 # Slightly adjusted wall repulsion
+- initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+- final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+-
+- best_sum_radii = -1.0
+- best_final_centers = None
+-
+- # Candidate initial positions for the 26th circle to optimize corner utilization.
+- candidate_extra_circle_initial_positions = [
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+- [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+- ]
+-
+- for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+- # --- Initialization for current trial ---
+- current_centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- current_centers[k, 0] = (i + 0.5) * spacing
+- current_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+- current_centers[25] = initial_pos_26th_circle
+-
+- centers_for_sim = current_centers.copy()
+-
+- # --- Main Simulation Loop (Vectorized) ---
+- for sim_iter in range(iterations):
+- radii = compute_max_radii(centers_for_sim)
+-
+- # Anneal growth pressure quadratically
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * \
+- (1.0 - (sim_iter / iterations))**2
+- inflated_radii = radii * current_growth_pressure
+-
+- # --- Vectorized Force Calculation ---
+- # a. Circle-to-circle repulsion forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_inflated_radii - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- # b. Vectorized Wall repulsion forces
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = inflated_radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+- overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces = circle_forces + wall_forces
+-
+- # --- Update center positions ---
+- current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+- centers_for_sim += forces * current_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # --- Finalization for current trial ---
+- current_final_radii = compute_max_radii(centers_for_sim)
+- current_sum_radii = np.sum(current_final_radii)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_final_centers = centers_for_sim.copy()
+-
+- # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 300 # Increased fine-tune iterations for precision
+- fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
+- finetune_growth_pressure_start = 1.0005 # Small growth pressure in fine-tune
+- finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
+- centers_for_sim = best_final_centers.copy()
+-
+- for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+- radii = compute_max_radii(centers_for_sim)
+-
+- # Anneal growth pressure for fine-tuning (linear decay for stability)
+- current_finetune_growth_pressure = finetune_growth_pressure_start - \
+- (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+- (sim_iter_ft / fine_tune_iterations)
+- inflated_radii_ft = radii * current_finetune_growth_pressure
+-
+- # --- Vectorized Force Calculation ---
+- # a. Circle-to-circle forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+-
+- overlaps = np.maximum(0, sum_radii_pairs - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- # b. Wall forces (also use inflated radii for consistency with growth pressure)
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+- overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces = circle_forces + wall_forces
+-
+- # Anneal learning rate for fine-tuning (linear decay)
+- current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+- centers_for_sim += forces * current_finetune_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- final_centers = centers_for_sim
+- final_radii = compute_max_radii(final_centers)
+-
+- return final_centers, final_radii
+-
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
++ # Iterations for the radii calculation, for robust convergence
++ # This part of the code is intentionally kept as-is, as it's a core utility function
++ # that is not part of the GA's 'evolutionary' algorithm itself, but rather its fitness evaluation.
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+- if dist < 1e-9:
++ if dist < 1e-9: # Centers are practically identical
++ # To prevent division by zero and ensure no overlap, assign zero radius
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+- return np.maximum(radii, 0.0)
++ return np.maximum(radii, 0.0) # Ensure no negative radii
+
++
++def construct_packing():
++ """
++ Constructs a packing of 26 circles using a Genetic Algorithm.
++
++ Returns:
++ Tuple of (centers, radii)
++ centers: np.array of shape (26, 2) with (x, y) coordinates
++ radii: np.array of shape (26) with radius of each circle
++ """
++ n = 26
++
++ # --- Genetic Algorithm Parameters ---
++ POPULATION_SIZE = 100
++ GENERATIONS = 500
++ MUTATION_RATE = 0.2 # Probability of a circle's coordinates being mutated
++ CROSSOVER_RATE = 0.8 # Probability of two parents undergoing crossover
++ MUTATION_STRENGTH_START = 0.1 # Initial std dev for Gaussian mutation
++ MUTATION_STRENGTH_END = 0.005 # Final std dev for Gaussian mutation (annealed)
++ ELITE_COUNT = 5 # Number of best individuals to carry over directly
++ TOURNAMENT_SIZE = 5 # Number of individuals in a tournament for parent selection
++
++ # --- Initialization ---
++ population = []
++ for i in range(POPULATION_SIZE):
++ if i < POPULATION_SIZE // 4: # Some individuals start from a perturbed grid
++ # 5x5 grid for 25 circles
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ initial_centers = np.zeros((n, 2))
++ k = 0
++ for row in range(num_cells_side):
++ for col in range(num_cells_side):
++ initial_centers[k, 0] = (col + 0.5) * spacing
++ initial_centers[k, 1] = (row + 0.5) * spacing
++ k += 1
++ # Perturb the grid slightly
++ initial_centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
++ initial_centers[:25, :] = np.clip(initial_centers[:25, :], 0.0, 1.0)
++ # Place the 26th circle at an interstitial point for initial diversity, with small perturbation
++ if n > 25:
++ initial_centers[25] = [spacing, spacing] + np.random.normal(0, spacing * 0.02, size=2)
++ initial_centers[25] = np.clip(initial_centers[25], 0.0, 1.0)
++ population.append(initial_centers)
++ else: # Most individuals start completely randomly for broader exploration
++ population.append(np.random.rand(n, 2))
++
++
++ best_individual_centers = None
++ best_overall_fitness = -1.0
++
++ # --- Main Genetic Algorithm Loop ---
++ for generation in range(GENERATIONS):
++ # Calculate fitness for each individual
++ fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in population])
++
++ # Track the best individual in the current population
++ current_best_idx = np.argmax(fitnesses)
++ current_best_fitness = fitnesses[current_best_idx]
++
++ if current_best_fitness > best_overall_fitness:
++ best_overall_fitness = current_best_fitness
++ best_individual_centers = population[current_best_idx].copy()
++
++ # Create the next generation
++ new_population = []
++
++ # Elitism: Carry over the best individuals directly
++ elite_indices = np.argsort(fitnesses)[-ELITE_COUNT:]
++ for idx in elite_indices:
++ new_population.append(population[idx].copy())
++
++ # Anneal mutation strength for exploration-exploitation trade-off
++ # Cubic decay ensures faster reduction of mutation strength towards fine-tuning
++ mutation_strength = MUTATION_STRENGTH_END + \
++ (MUTATION_STRENGTH_START - MUTATION_STRENGTH_END) * \
++ (1.0 - (generation / GENERATIONS))**1.5
++
++ # Fill the rest of the new population
++ while len(new_population) < POPULATION_SIZE:
++ # Selection: Choose two parents using tournament selection
++ parent1 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
++ parent2 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
++
++ # Crossover: Combine genetic material from parents
++ if np.random.rand() < CROSSOVER_RATE:
++ child = arithmetic_crossover(parent1, parent2)
++ else: # If no crossover, one parent (e.g., parent1) becomes the child
++ child = parent1.copy()
++
++ # Mutation: Introduce random changes
++ child = gaussian_mutate(child, MUTATION_RATE, mutation_strength)
++
++ new_population.append(child)
++
++ population = new_population
++
++ # After all generations, return the best found individual
++ final_centers = best_individual_centers
++ final_radii = compute_max_radii(final_centers)
++
++ return final_centers, final_radii
++
++
++def select_parent(population, fitnesses, tournament_size):
++ """
++ Selects a parent using tournament selection.
++ """
++ tournament_indices = np.random.choice(len(population), size=tournament_size, replace=False)
++ tournament_fitnesses = fitnesses[tournament_indices]
++ winner_index_in_tournament = np.argmax(tournament_fitnesses)
++ winner_original_index = tournament_indices[winner_index_in_tournament]
++ return population[winner_original_index]
++
++def arithmetic_crossover(parent1, parent2):
++ """
++ Performs arithmetic crossover for continuous variables.
++ Child = alpha * Parent1 + (1-alpha) * Parent2
++ """
++ alpha = np.random.uniform(0.3, 0.7) # Blend factor for averaging parents
++ child = alpha * parent1 + (1 - alpha) * parent2
++ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
++
++def gaussian_mutate(individual, mutation_rate, mutation_strength):
++ """
++ Applies Gaussian mutation to an individual's circle centers.
++ Each circle's coordinates have a `mutation_rate` chance of being perturbed.
++ """
++ mutated_individual = individual.copy()
++ num_circles = individual.shape[0]
++
++ # Iterate through each circle and apply mutation based on mutation_rate
++ for i in range(num_circles):
++ if np.random.rand() < mutation_rate:
++ mutated_individual[i, 0] += np.random.normal(0, mutation_strength)
++ mutated_individual[i, 1] += np.random.normal(0, mutation_strength)
++
++ # Introduce a small chance for a "strong" mutation (random reset)
++ # This helps escape local optima by completely changing a circle's position
++ if np.random.rand() < 0.01: # 1% chance for a strong mutation
++ idx_to_reset = np.random.randint(0, num_circles)
++ mutated_individual[idx_to_reset, :] = np.random.rand(2) # New random position
++
++ return np.clip(mutated_individual, 0.0, 1.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..30f34f3a8f19e8ccec64b3ac3a0830432b7c4aca
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/main.py
@@ -0,0 +1,197 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Iterations for the radii calculation, for robust convergence
+ # This part of the code is intentionally kept as-is, as it's a core utility function
+ # that is not part of the GA's 'evolutionary' algorithm itself, but rather its fitness evaluation.
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ # To prevent division by zero and ensure no overlap, assign zero radius
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Genetic Algorithm.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Genetic Algorithm Parameters ---
+ POPULATION_SIZE = 100
+ GENERATIONS = 500
+ MUTATION_RATE = 0.2 # Probability of a circle's coordinates being mutated
+ CROSSOVER_RATE = 0.8 # Probability of two parents undergoing crossover
+ MUTATION_STRENGTH_START = 0.1 # Initial std dev for Gaussian mutation
+ MUTATION_STRENGTH_END = 0.005 # Final std dev for Gaussian mutation (annealed)
+ ELITE_COUNT = 5 # Number of best individuals to carry over directly
+ TOURNAMENT_SIZE = 5 # Number of individuals in a tournament for parent selection
+
+ # --- Initialization ---
+ population = []
+ for i in range(POPULATION_SIZE):
+ if i < POPULATION_SIZE // 4: # Some individuals start from a perturbed grid
+ # 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ initial_centers = np.zeros((n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ # Perturb the grid slightly
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+ initial_centers[:25, :] = np.clip(initial_centers[:25, :], 0.0, 1.0)
+ # Place the 26th circle at an interstitial point for initial diversity, with small perturbation
+ if n > 25:
+ initial_centers[25] = [spacing, spacing] + np.random.normal(0, spacing * 0.02, size=2)
+ initial_centers[25] = np.clip(initial_centers[25], 0.0, 1.0)
+ population.append(initial_centers)
+ else: # Most individuals start completely randomly for broader exploration
+ population.append(np.random.rand(n, 2))
+
+
+ best_individual_centers = None
+ best_overall_fitness = -1.0
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(GENERATIONS):
+ # Calculate fitness for each individual
+ fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in population])
+
+ # Track the best individual in the current population
+ current_best_idx = np.argmax(fitnesses)
+ current_best_fitness = fitnesses[current_best_idx]
+
+ if current_best_fitness > best_overall_fitness:
+ best_overall_fitness = current_best_fitness
+ best_individual_centers = population[current_best_idx].copy()
+
+ # Create the next generation
+ new_population = []
+
+ # Elitism: Carry over the best individuals directly
+ elite_indices = np.argsort(fitnesses)[-ELITE_COUNT:]
+ for idx in elite_indices:
+ new_population.append(population[idx].copy())
+
+ # Anneal mutation strength for exploration-exploitation trade-off
+ # Cubic decay ensures faster reduction of mutation strength towards fine-tuning
+ mutation_strength = MUTATION_STRENGTH_END + \
+ (MUTATION_STRENGTH_START - MUTATION_STRENGTH_END) * \
+ (1.0 - (generation / GENERATIONS))**1.5
+
+ # Fill the rest of the new population
+ while len(new_population) < POPULATION_SIZE:
+ # Selection: Choose two parents using tournament selection
+ parent1 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+ parent2 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+
+ # Crossover: Combine genetic material from parents
+ if np.random.rand() < CROSSOVER_RATE:
+ child = arithmetic_crossover(parent1, parent2)
+ else: # If no crossover, one parent (e.g., parent1) becomes the child
+ child = parent1.copy()
+
+ # Mutation: Introduce random changes
+ child = gaussian_mutate(child, MUTATION_RATE, mutation_strength)
+
+ new_population.append(child)
+
+ population = new_population
+
+ # After all generations, return the best found individual
+ final_centers = best_individual_centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def select_parent(population, fitnesses, tournament_size):
+ """
+ Selects a parent using tournament selection.
+ """
+ tournament_indices = np.random.choice(len(population), size=tournament_size, replace=False)
+ tournament_fitnesses = fitnesses[tournament_indices]
+ winner_index_in_tournament = np.argmax(tournament_fitnesses)
+ winner_original_index = tournament_indices[winner_index_in_tournament]
+ return population[winner_original_index]
+
+def arithmetic_crossover(parent1, parent2):
+ """
+ Performs arithmetic crossover for continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2
+ """
+ alpha = np.random.uniform(0.3, 0.7) # Blend factor for averaging parents
+ child = alpha * parent1 + (1 - alpha) * parent2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+def gaussian_mutate(individual, mutation_rate, mutation_strength):
+ """
+ Applies Gaussian mutation to an individual's circle centers.
+ Each circle's coordinates have a `mutation_rate` chance of being perturbed.
+ """
+ mutated_individual = individual.copy()
+ num_circles = individual.shape[0]
+
+ # Iterate through each circle and apply mutation based on mutation_rate
+ for i in range(num_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_individual[i, 0] += np.random.normal(0, mutation_strength)
+ mutated_individual[i, 1] += np.random.normal(0, mutation_strength)
+
+ # Introduce a small chance for a "strong" mutation (random reset)
+ # This helps escape local optima by completely changing a circle's position
+ if np.random.rand() < 0.01: # 1% chance for a strong mutation
+ idx_to_reset = np.random.randint(0, num_circles)
+ mutated_individual[idx_to_reset, :] = np.random.rand(2) # New random position
+
+ return np.clip(mutated_individual, 0.0, 1.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d4d68d3cdc63b54772c63b3327f0a823d19d77c3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/original.py
@@ -0,0 +1,210 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters based on successful class-based implementation (G37 equivalent)
+ iterations = 1200 # Increased for more thorough exploration
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.3 # Slightly adjusted wall repulsion
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 300 # Increased fine-tune iterations for precision
+ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
+ finetune_growth_pressure_start = 1.0005 # Small growth pressure in fine-tune
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure for fine-tuning (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..fa5418472976e6ad7a7d1a6ceca2d11437c664c0
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results
+Run 1/1 completed in 132.09 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.2215527738335212
+ public: {'centers_str': ' centers[0] = (0.1109, 0.1108)\n centers[1] = (0.3211, 0.0705)\n centers[2] = (0.4976, 0.1032)\n centers[3] = (0.7210, 0.0627)\n centers[4] = (0.8842, 0.1011)\n centers[5] = (0.0488, 0.3372)\n centers[6] = (0.4388, 0.4451)\n centers[7] = (0.5972, 0.6634)\n centers[8] = (0.6407, 0.2561)\n centers[9] = (0.9166, 0.2820)\n centers[10] = (0.1029, 0.4915)\n centers[11] = (0.3138, 0.5033)\n centers[12] = (0.4646, 0.5699)\n centers[13] = (0.6268, 0.5114)\n centers[14] = (0.8662, 0.4935)\n centers[15] = (0.1108, 0.7125)\n centers[16] = (0.3228, 0.6665)\n centers[17] = (0.5096, 0.7179)\n centers[18] = (0.7125, 0.7073)\n centers[19] = (0.9147, 0.7063)\n centers[20] = (0.0873, 0.9127)\n centers[21] = (0.2875, 0.8842)\n centers[22] = (0.5051, 0.9029)\n centers[23] = (0.6972, 0.9105)\n centers[24] = (0.8949, 0.8958)\n centers[25] = (0.2588, 0.2897)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.2215527738335212}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/packing_viz.png
+ execution_time_mean: 132.09364925604314
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..0033d2b2ff2a35051b8d81163642f2cbb44b89a9
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.2215527738335212,
+ "public": {
+ "centers_str": " centers[0] = (0.1109, 0.1108)\n centers[1] = (0.3211, 0.0705)\n centers[2] = (0.4976, 0.1032)\n centers[3] = (0.7210, 0.0627)\n centers[4] = (0.8842, 0.1011)\n centers[5] = (0.0488, 0.3372)\n centers[6] = (0.4388, 0.4451)\n centers[7] = (0.5972, 0.6634)\n centers[8] = (0.6407, 0.2561)\n centers[9] = (0.9166, 0.2820)\n centers[10] = (0.1029, 0.4915)\n centers[11] = (0.3138, 0.5033)\n centers[12] = (0.4646, 0.5699)\n centers[13] = (0.6268, 0.5114)\n centers[14] = (0.8662, 0.4935)\n centers[15] = (0.1108, 0.7125)\n centers[16] = (0.3228, 0.6665)\n centers[17] = (0.5096, 0.7179)\n centers[18] = (0.7125, 0.7073)\n centers[19] = (0.9147, 0.7063)\n centers[20] = (0.0873, 0.9127)\n centers[21] = (0.2875, 0.8842)\n centers[22] = (0.5051, 0.9029)\n centers[23] = (0.6972, 0.9105)\n centers[24] = (0.8949, 0.8958)\n centers[25] = (0.2588, 0.2897)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.2215527738335212
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/results/packing_viz.png",
+ "execution_time_mean": 132.09364925604314,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..76354c1f537d46bd7dc16c5cd65e3a3492a9c14f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_55/rewrite.txt
@@ -0,0 +1,188 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Iterations for the radii calculation, for robust convergence
+ # This part of the code is intentionally kept as-is, as it's a core utility function
+ # that is not part of the GA's 'evolutionary' algorithm itself, but rather its fitness evaluation.
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ # To prevent division by zero and ensure no overlap, assign zero radius
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Genetic Algorithm.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Genetic Algorithm Parameters ---
+ POPULATION_SIZE = 100
+ GENERATIONS = 500
+ MUTATION_RATE = 0.2 # Probability of a circle's coordinates being mutated
+ CROSSOVER_RATE = 0.8 # Probability of two parents undergoing crossover
+ MUTATION_STRENGTH_START = 0.1 # Initial std dev for Gaussian mutation
+ MUTATION_STRENGTH_END = 0.005 # Final std dev for Gaussian mutation (annealed)
+ ELITE_COUNT = 5 # Number of best individuals to carry over directly
+ TOURNAMENT_SIZE = 5 # Number of individuals in a tournament for parent selection
+
+ # --- Initialization ---
+ population = []
+ for i in range(POPULATION_SIZE):
+ if i < POPULATION_SIZE // 4: # Some individuals start from a perturbed grid
+ # 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ initial_centers = np.zeros((n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ # Perturb the grid slightly
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+ initial_centers[:25, :] = np.clip(initial_centers[:25, :], 0.0, 1.0)
+ # Place the 26th circle at an interstitial point for initial diversity, with small perturbation
+ if n > 25:
+ initial_centers[25] = [spacing, spacing] + np.random.normal(0, spacing * 0.02, size=2)
+ initial_centers[25] = np.clip(initial_centers[25], 0.0, 1.0)
+ population.append(initial_centers)
+ else: # Most individuals start completely randomly for broader exploration
+ population.append(np.random.rand(n, 2))
+
+
+ best_individual_centers = None
+ best_overall_fitness = -1.0
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(GENERATIONS):
+ # Calculate fitness for each individual
+ fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in population])
+
+ # Track the best individual in the current population
+ current_best_idx = np.argmax(fitnesses)
+ current_best_fitness = fitnesses[current_best_idx]
+
+ if current_best_fitness > best_overall_fitness:
+ best_overall_fitness = current_best_fitness
+ best_individual_centers = population[current_best_idx].copy()
+
+ # Create the next generation
+ new_population = []
+
+ # Elitism: Carry over the best individuals directly
+ elite_indices = np.argsort(fitnesses)[-ELITE_COUNT:]
+ for idx in elite_indices:
+ new_population.append(population[idx].copy())
+
+ # Anneal mutation strength for exploration-exploitation trade-off
+ # Cubic decay ensures faster reduction of mutation strength towards fine-tuning
+ mutation_strength = MUTATION_STRENGTH_END + \
+ (MUTATION_STRENGTH_START - MUTATION_STRENGTH_END) * \
+ (1.0 - (generation / GENERATIONS))**1.5
+
+ # Fill the rest of the new population
+ while len(new_population) < POPULATION_SIZE:
+ # Selection: Choose two parents using tournament selection
+ parent1 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+ parent2 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+
+ # Crossover: Combine genetic material from parents
+ if np.random.rand() < CROSSOVER_RATE:
+ child = arithmetic_crossover(parent1, parent2)
+ else: # If no crossover, one parent (e.g., parent1) becomes the child
+ child = parent1.copy()
+
+ # Mutation: Introduce random changes
+ child = gaussian_mutate(child, MUTATION_RATE, mutation_strength)
+
+ new_population.append(child)
+
+ population = new_population
+
+ # After all generations, return the best found individual
+ final_centers = best_individual_centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def select_parent(population, fitnesses, tournament_size):
+ """
+ Selects a parent using tournament selection.
+ """
+ tournament_indices = np.random.choice(len(population), size=tournament_size, replace=False)
+ tournament_fitnesses = fitnesses[tournament_indices]
+ winner_index_in_tournament = np.argmax(tournament_fitnesses)
+ winner_original_index = tournament_indices[winner_index_in_tournament]
+ return population[winner_original_index]
+
+def arithmetic_crossover(parent1, parent2):
+ """
+ Performs arithmetic crossover for continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2
+ """
+ alpha = np.random.uniform(0.3, 0.7) # Blend factor for averaging parents
+ child = alpha * parent1 + (1 - alpha) * parent2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+def gaussian_mutate(individual, mutation_rate, mutation_strength):
+ """
+ Applies Gaussian mutation to an individual's circle centers.
+ Each circle's coordinates have a `mutation_rate` chance of being perturbed.
+ """
+ mutated_individual = individual.copy()
+ num_circles = individual.shape[0]
+
+ # Iterate through each circle and apply mutation based on mutation_rate
+ for i in range(num_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_individual[i, 0] += np.random.normal(0, mutation_strength)
+ mutated_individual[i, 1] += np.random.normal(0, mutation_strength)
+
+ # Introduce a small chance for a "strong" mutation (random reset)
+ # This helps escape local optima by completely changing a circle's position
+ if np.random.rand() < 0.01: # 1% chance for a strong mutation
+ idx_to_reset = np.random.randint(0, num_circles)
+ mutated_individual[idx_to_reset, :] = np.random.rand(2) # New random position
+
+ return np.clip(mutated_individual, 0.0, 1.0)
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1b5699ff5e5c6e879719556f5de3bbf600f73a2d
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..b5eedd27f75b94b85e0d9f80dcd4f59efd74a916
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/edit.diff
@@ -0,0 +1,318 @@
+--- a/original.py
++++ b/original.py
+@@ -1,223 +1,251 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+- def _initialize_centers(self):
+- """Sets up the initial circle positions using the best-known static 5x5 grid."""
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for j in range(num_cells_side):
+- for i in range(num_cells_side):
+- self.centers[k, 0] = (i + 0.5) * spacing
+- self.centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+- # Place the 26th circle in the optimal interstitial void found in prior runs.
+- self.centers[25] = [spacing, spacing]
+-
+- def _compute_radii_for_centers(self, centers_to_eval):
++ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+- for _ in range(self.config['radius_iter']):
++ for _ in range(iterations):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+- current_radii = self._compute_radii_for_centers(self.centers)
++ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+- # b) Wall repulsion
+- for i in range(self.n):
+- overlap_l = pressured_radii[i] - self.centers[i, 0]
+- if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+-
+- overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+- if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+-
+- overlap_b = pressured_radii[i] - self.centers[i, 1]
+- if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+-
+- overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+- if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
++ # b) Wall repulsion (fully vectorized)
++ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0]) # Left wall
++ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0) # Right wall
++ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1]) # Bottom wall
++ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+- current_radii = self._compute_radii_for_centers(self.centers)
++ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b) Wall repulsion (based on actual radii)
+- for i in range(self.n):
+- overlap_l = current_radii[i] - self.centers[i, 0]
+- if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+-
+- overlap_r = (self.centers[i, 0] + current_radii[i]) - 1.0
+- if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+-
+- overlap_b = current_radii[i] - self.centers[i, 1]
+- if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+-
+- overlap_t = (self.centers[i, 1] + current_radii[i]) - 1.0
+- if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
++ # b) Wall repulsion (fully vectorized, based on actual radii)
++ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
++ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
++ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
++ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+- def pack(self):
++ def pack(self, initial_centers):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+- self._initialize_centers()
++ self.centers = initial_centers.copy()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+- # Final computation of radii for the refined centers.
+- self.radii = self._compute_radii_for_centers(self.centers)
++ # Final computation of radii for the refined centers with high precision.
++ self.radii = self._compute_radii_for_centers(self.centers, self.config['final_radius_iter'])
+ return self.centers, self.radii
+
+ def construct_packing():
+ """
+- Main function to construct the circle packing. This implementation shifts from
+- a procedural script to an object-oriented approach by using the
+- ConfigurableHybridPacker class to manage the packing process.
+- """
+- # Hyperparameters are now centralized in a configuration dictionary.
+- # We introduce an annealing schedule for growth pressure to break out of
+- # the initial grid's local minimum and a final fine-tuning phase.
++ Main function to construct the circle packing. This implementation uses a multi-start
++ strategy with a ConfigurableHybridPacker to explore various initial configurations
++ and select the best one.
++ """
++ n = 26
++ # Hyperparameters for the force-directed simulation
+ packer_config = {
+- 'sim_iter': 1000, # Increased iterations for thorough refinement.
+- 'radius_iter': 250, # Sufficient iterations for radius convergence.
+- 'learning_rate': 0.02, # A balanced learning rate for exploration.
++ 'sim_iter': 3000, # Increased iterations for thorough refinement.
++ 'radius_sim_iter': 200, # Iterations for radius calculation during simulation (speed).
++ 'final_radius_iter': 750, # Iterations for final radius calculation (precision).
++ 'learning_rate': 0.015, # Slightly adjusted learning rate.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+- 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+- 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+- 'fine_tune_iter': 200, # Increased iterations for final settlement.
++ 'initial_growth_pressure': 1.03, # Start with stronger pressure to 'unfreeze' the grid.
++ 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
++ 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+- packer = ConfigurableHybridPacker(n=26, config=packer_config)
+- centers, radii = packer.pack()
+-
+- return centers, radii
++ # Initial 5x5 grid setup for 25 circles
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ base_centers = np.zeros((n - 1, 2)) # 25 circles
++ k = 0
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ base_centers[k, 0] = (i + 0.5) * spacing
++ base_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ # Strategic candidate placements for the 26th circle.
++ # Includes interstitial points, corner-offset points, and central locations.
++ candidate_extra_positions = [
++ [spacing, spacing], # (0.2, 0.2) - primary interstitial
++ [spacing, 1 - spacing], # (0.2, 0.8)
++ [1 - spacing, spacing], # (0.8, 0.2)
++ [1 - spacing, 1 - spacing], # (0.8, 0.8)
++ [0.5, 0.5], # Center of the square
++ [spacing, 0.5], # Mid-edge interstitial
++ [0.5, spacing],
++ [1 - spacing, 0.5],
++ [0.5, 1 - spacing],
++ [0.05, 0.05], [0.05, 0.95], # Near corners
++ [0.95, 0.05], [0.95, 0.95],
++ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Slightly more away from exact corner
++ [0.2, 0.4], [0.4, 0.2] # Additional interstitial points
++ ]
++
++ best_sum_radii = -1.0
++ best_centers = None
++ best_radii = None
++
++ # Iterate through candidate positions for the 26th circle and run the full packing
++ for extra_pos in candidate_extra_positions:
++ current_initial_centers = np.zeros((n, 2))
++ current_initial_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
++ current_initial_centers[n-1] = np.array(extra_pos)
++
++ # Create a new packer instance for each initial configuration
++ packer = ConfigurableHybridPacker(n=n, config=packer_config)
++
++ centers_candidate, radii_candidate = packer.pack(current_initial_centers)
++ current_sum_radii = np.sum(radii_candidate)
++
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_centers = centers_candidate.copy()
++ best_radii = radii_candidate.copy()
++
++ # Fallback if no valid configuration was found (should not happen with comprehensive candidates)
++ if best_centers is None:
++ # Default to a simple initial setup and run if all candidate positions fail unexpectedly
++ default_initial_centers = np.zeros((n, 2))
++ default_initial_centers[:n-1] = base_centers
++ default_initial_centers[n-1] = [0.5, 0.5] # Central placement for 26th circle
++ packer = ConfigurableHybridPacker(n=n, config=packer_config)
++ best_centers, best_radii = packer.pack(default_initial_centers)
++
++ return best_centers, best_radii
++
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..0248cb1de05afd62ae03aeba26e0d2683d276f70
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/main.py
@@ -0,0 +1,251 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(iterations):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0]) # Left wall
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0) # Right wall
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1]) # Bottom wall
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self, initial_centers):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self.centers = initial_centers.copy()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers with high precision.
+ self.radii = self._compute_radii_for_centers(self.centers, self.config['final_radius_iter'])
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation uses a multi-start
+ strategy with a ConfigurableHybridPacker to explore various initial configurations
+ and select the best one.
+ """
+ n = 26
+ # Hyperparameters for the force-directed simulation
+ packer_config = {
+ 'sim_iter': 3000, # Increased iterations for thorough refinement.
+ 'radius_sim_iter': 200, # Iterations for radius calculation during simulation (speed).
+ 'final_radius_iter': 750, # Iterations for final radius calculation (precision).
+ 'learning_rate': 0.015, # Slightly adjusted learning rate.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.03, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
+ 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points, corner-offset points, and central locations.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2) - primary interstitial
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.5, 0.5], # Center of the square
+ [spacing, 0.5], # Mid-edge interstitial
+ [0.5, spacing],
+ [1 - spacing, 0.5],
+ [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], # Near corners
+ [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Slightly more away from exact corner
+ [0.2, 0.4], [0.4, 0.2] # Additional interstitial points
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle and run the full packing
+ for extra_pos in candidate_extra_positions:
+ current_initial_centers = np.zeros((n, 2))
+ current_initial_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+ current_initial_centers[n-1] = np.array(extra_pos)
+
+ # Create a new packer instance for each initial configuration
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+
+ centers_candidate, radii_candidate = packer.pack(current_initial_centers)
+ current_sum_radii = np.sum(radii_candidate)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_candidate.copy()
+ best_radii = radii_candidate.copy()
+
+ # Fallback if no valid configuration was found (should not happen with comprehensive candidates)
+ if best_centers is None:
+ # Default to a simple initial setup and run if all candidate positions fail unexpectedly
+ default_initial_centers = np.zeros((n, 2))
+ default_initial_centers[:n-1] = base_centers
+ default_initial_centers[n-1] = [0.5, 0.5] # Central placement for 26th circle
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+ best_centers, best_radii = packer.pack(default_initial_centers)
+
+ return best_centers, best_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..366062572268f1157bc37130701427d81ac9bdd1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/original.py
@@ -0,0 +1,223 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion
+ for i in range(self.n):
+ overlap_l = pressured_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = pressured_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (based on actual radii)
+ for i in range(self.n):
+ overlap_l = current_radii[i] - self.centers[i, 0]
+ if overlap_l > 0: forces[i, 0] += wall_strength * overlap_l
+
+ overlap_r = (self.centers[i, 0] + current_radii[i]) - 1.0
+ if overlap_r > 0: forces[i, 0] -= wall_strength * overlap_r
+
+ overlap_b = current_radii[i] - self.centers[i, 1]
+ if overlap_b > 0: forces[i, 1] += wall_strength * overlap_b
+
+ overlap_t = (self.centers[i, 1] + current_radii[i]) - 1.0
+ if overlap_t > 0: forces[i, 1] -= wall_strength * overlap_t
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary.
+ # We introduce an annealing schedule for growth pressure to break out of
+ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+ 'sim_iter': 1000, # Increased iterations for thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 200, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..50d9fb6305abe5c901bb30500fc840f915ce46a7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results
+Run 1/1 completed in 184.20 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.0017204032053804
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.2784, 0.0778)\n centers[2] = (0.5236, 0.0777)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8979, 0.1017)\n centers[5] = (0.1000, 0.3000)\n centers[6] = (0.2785, 0.3227)\n centers[7] = (0.5209, 0.3205)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.8996, 0.3034)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.6992, 0.4768)\n centers[14] = (0.9000, 0.5007)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.2762, 0.6904)\n centers[17] = (0.4629, 0.6858)\n centers[18] = (0.7131, 0.6860)\n centers[19] = (0.9286, 0.7015)\n centers[20] = (0.0895, 0.9002)\n centers[21] = (0.2933, 0.8856)\n centers[22] = (0.5154, 0.8926)\n centers[23] = (0.7072, 0.9153)\n centers[24] = (0.8957, 0.8956)\n centers[25] = (0.3987, 0.2013)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.0017204032053804}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/packing_viz.png
+ execution_time_mean: 184.19504989823326
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..b716f63e78b1c43ac839e9bd4bcad35d4b846bbc
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.0017204032053804,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.2784, 0.0778)\n centers[2] = (0.5236, 0.0777)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8979, 0.1017)\n centers[5] = (0.1000, 0.3000)\n centers[6] = (0.2785, 0.3227)\n centers[7] = (0.5209, 0.3205)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.8996, 0.3034)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.6992, 0.4768)\n centers[14] = (0.9000, 0.5007)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.2762, 0.6904)\n centers[17] = (0.4629, 0.6858)\n centers[18] = (0.7131, 0.6860)\n centers[19] = (0.9286, 0.7015)\n centers[20] = (0.0895, 0.9002)\n centers[21] = (0.2933, 0.8856)\n centers[22] = (0.5154, 0.8926)\n centers[23] = (0.7072, 0.9153)\n centers[24] = (0.8957, 0.8956)\n centers[25] = (0.3987, 0.2013)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.0017204032053804
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/results/packing_viz.png",
+ "execution_time_mean": 184.19504989823326,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9d8db6c992221aa66f3e55659d632b0e09c30bc6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_56/rewrite.txt
@@ -0,0 +1,242 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(iterations):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0]) # Left wall
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0) # Right wall
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1]) # Bottom wall
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self, initial_centers):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self.centers = initial_centers.copy()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers with high precision.
+ self.radii = self._compute_radii_for_centers(self.centers, self.config['final_radius_iter'])
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation uses a multi-start
+ strategy with a ConfigurableHybridPacker to explore various initial configurations
+ and select the best one.
+ """
+ n = 26
+ # Hyperparameters for the force-directed simulation
+ packer_config = {
+ 'sim_iter': 3000, # Increased iterations for thorough refinement.
+ 'radius_sim_iter': 200, # Iterations for radius calculation during simulation (speed).
+ 'final_radius_iter': 750, # Iterations for final radius calculation (precision).
+ 'learning_rate': 0.015, # Slightly adjusted learning rate.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.03, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
+ 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points, corner-offset points, and central locations.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2) - primary interstitial
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.5, 0.5], # Center of the square
+ [spacing, 0.5], # Mid-edge interstitial
+ [0.5, spacing],
+ [1 - spacing, 0.5],
+ [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], # Near corners
+ [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Slightly more away from exact corner
+ [0.2, 0.4], [0.4, 0.2] # Additional interstitial points
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle and run the full packing
+ for extra_pos in candidate_extra_positions:
+ current_initial_centers = np.zeros((n, 2))
+ current_initial_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+ current_initial_centers[n-1] = np.array(extra_pos)
+
+ # Create a new packer instance for each initial configuration
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+
+ centers_candidate, radii_candidate = packer.pack(current_initial_centers)
+ current_sum_radii = np.sum(radii_candidate)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_candidate.copy()
+ best_radii = radii_candidate.copy()
+
+ # Fallback if no valid configuration was found (should not happen with comprehensive candidates)
+ if best_centers is None:
+ # Default to a simple initial setup and run if all candidate positions fail unexpectedly
+ default_initial_centers = np.zeros((n, 2))
+ default_initial_centers[:n-1] = base_centers
+ default_initial_centers[n-1] = [0.5, 0.5] # Central placement for 26th circle
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+ best_centers, best_radii = packer.pack(default_initial_centers)
+
+ return best_centers, best_radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c18f853c2f13b04067e53e39edaa4f418ad27970
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..5e8e56352033dbb5d092557da85d194557629df8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/edit.diff
@@ -0,0 +1,488 @@
+--- a/original.py
++++ b/original.py
+@@ -1,218 +1,297 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+-This version is a crossover, combining the high-level strategy of the best-performing
+-parent (multi-start, two-phase simulation, annealed growth pressure) with the
+-efficient vectorized force calculations from the inspiration program.
++This version redesigns the program using a robust, modular, class-based architecture
++to improve maintainability, parameter tuning, and extensibility. It centralizes
++hyperparameter management and simulation logic within a single class, while
++implementing enhanced termination criteria for radius calculation.
+ """
+
+-import numpy as np
+-
+-def construct_packing():
+- """
+- Constructs a packing of 26 circles by simulating physical repulsion forces
+- to find an optimal arrangement of centers.
++# Helper function (remains outside the class as it's stateless and generic)
++def compute_max_radii(centers):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ This uses an iterative proportional scaling method with a dynamic convergence criterion.
++
++ Args:
++ centers (np.array): Nx2 array of circle center coordinates.
+
+ Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- n = 26
+-
+- # --- Hyperparameters for the simulation ---
+- # Tuned hyperparameters based on successful prior implementations
+- iterations = 1200 # Increased for more thorough exploration
+- learning_rate = 0.01
+- wall_repulsion_strength = 0.3 # Adjusted wall repulsion for better boundary interaction
+- initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+- final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+-
+- best_sum_radii = -1.0
+- best_final_centers = None
+-
+- # Candidate initial positions for the 26th circle to optimize corner utilization.
+- candidate_extra_circle_initial_positions = [
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+- [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+- ]
+-
+- for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+- # --- Initialization for current trial ---
+- current_centers = np.zeros((n, 2))
++ np.array: N-element array of radii for each circle.
++ """
++ n = centers.shape[0]
++ radii = np.ones(n) # Initialize with placeholder large radii
++
++ # 1. Limit by distance to square borders
++ for i in range(n):
++ x, y = centers[i]
++ radii[i] = min(x, y, 1 - x, 1 - y)
++
++ # 2. Limit by distance to other circles using iterative proportional scaling
++ # Recommendation 5: Refine `compute_max_radii` with a More Robust Dynamic Convergence Criterion
++ max_iterations = 500
++ min_radius_change = 1e-7 # Epsilon for detecting convergence
++ stable_iterations_required = 5 # Number of consecutive stable iterations
++
++ stable_iterations_count = 0
++
++ for iter_idx in range(max_iterations):
++ updated_in_pass = False
++ radii_before_pass = radii.copy() # Store radii before this pass for change detection
++
++ for i in range(n):
++ for j in range(i + 1, n):
++ dist = np.linalg.norm(centers[i] - centers[j])
++
++ if dist < 1e-12: # Handle extremely close or identical centers
++ # If centers are too close, effectively make their radii 0 to avoid NaNs or infinite forces
++ # and allow them to move apart.
++ radii[i] = 0.0
++ radii[j] = 0.0
++ updated_in_pass = True
++ continue
++
++ if radii[i] + radii[j] > dist:
++ updated_in_pass = True
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++
++ # Check for convergence after all pairwise adjustments in a pass
++ max_change_in_pass = np.max(np.abs(radii - radii_before_pass))
++
++ if max_change_in_pass < min_radius_change:
++ stable_iterations_count += 1
++ if stable_iterations_count >= stable_iterations_required:
++ break # Converged
++ else:
++ stable_iterations_count = 0 # Reset if a significant change occurred
++
++ return np.maximum(radii, 0.0) # Ensure no negative radii
++
++class CirclePackingOptimizer:
++ """
++ Encapsulates the logic for optimizing circle packing using a physical simulation approach.
++ Manages hyperparameters, initialization, force calculations, and simulation phases.
++ """
++ def __init__(self, n=26,
++ iterations_main=1500, learning_rate_main=0.015,
++ initial_growth_pressure=1.03, final_growth_pressure=1.0001,
++ wall_repulsion_strength=0.35,
++ fine_tune_iterations=400, fine_tune_learning_rate=0.001,
++ fine_tune_growth_pressure_start=1.0005, fine_tune_growth_pressure_end=1.00001,
++ initial_perturbation_scale=0.005): # Recommendation 4: perturbation scale
++
++ self.n = n
++ self.iterations_main = iterations_main
++ self.learning_rate_main = learning_rate_main
++ self.initial_growth_pressure = initial_growth_pressure
++ self.final_growth_pressure = final_growth_pressure
++ self.wall_repulsion_strength = wall_repulsion_strength
++ self.fine_tune_iterations = fine_tune_iterations
++ self.fine_tune_learning_rate = fine_tune_learning_rate
++ self.fine_tune_growth_pressure_start = fine_tune_growth_pressure_start
++ self.fine_tune_growth_pressure_end = fine_tune_growth_pressure_end
++ self.initial_perturbation_scale = initial_perturbation_scale
++
++ self.best_sum_radii = -1.0
++ self.best_final_centers = None
++
++ def _initialize_centers(self, extra_pos_26th_circle):
++ """
++ Initializes centers with a 5x5 grid and places the 26th circle.
++ Applies a small random perturbation.
++ (Implements Recommendation 1 by having flexible initialization and perturbation)
++ """
++ initial_grid_centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+- current_centers[k, 0] = (i + 0.5) * spacing
+- current_centers[k, 1] = (j + 0.5) * spacing
++ initial_grid_centers[k, 0] = (i + 0.5) * spacing
++ initial_grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+- # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+- perturbation_scale = 0.002 # A small fraction of the spacing
+- current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+-
+- current_centers[25] = initial_pos_26th_circle
+-
++ # Perturb the first 25 circles (the 5x5 grid)
++ initial_grid_centers[:25, :] += np.random.normal(0, spacing * self.initial_perturbation_scale, size=(25, 2))
++
++ # Place the 26th circle at the candidate position
++ initial_grid_centers[25] = extra_pos_26th_circle
++
+ # Ensure all centers stay within bounds after initial setup and perturbation
+- current_centers = np.clip(current_centers, 0.0, 1.0)
+-
+- centers_for_sim = current_centers.copy()
+-
+- # --- Main Simulation Loop (Vectorized) ---
++ centers = np.clip(initial_grid_centers, 0.0, 1.0)
++
++ return centers
++
++ def _calculate_forces(self, centers, radii, growth_pressure_factor):
++ """
++ Calculates total forces (circle-to-circle and wall repulsion) for a given state.
++ Uses vectorized operations for efficiency.
++ """
++ # Apply growth pressure to radii
++ inflated_radii = radii * growth_pressure_factor
++
++ # a. Circle-to-circle repulsion forces (Vectorized)
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
++
++ # Calculate overlaps, ensuring no self-overlap
++ overlaps = np.maximum(0, sum_inflated_radii - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ # Calculate unit vectors for directions
++ # Add a small epsilon to avoid division by zero for extremely small distances
++ # and handle potential NaN issues if distances are exactly zero.
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0 # Set forces to 0 for circles that are on top of each other
++
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ # b. Wall repulsion forces (Vectorized)
++ wall_forces = np.zeros_like(centers)
++
++ # Overlap with each wall
++ # Left wall (x=0)
++ overlap_left = inflated_radii - centers[:, 0]
++ # Right wall (x=1)
++ overlap_right = (centers[:, 0] + inflated_radii) - 1.0
++ # Bottom wall (y=0)
++ overlap_bottom = inflated_radii - centers[:, 1]
++ # Top wall (y=1)
++ overlap_top = (centers[:, 1] + inflated_radii) - 1.0
++
++ wall_forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, overlap_left)
++ wall_forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, overlap_right)
++ wall_forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, overlap_bottom)
++ wall_forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, overlap_top)
++
++ total_forces = circle_forces + wall_forces
++ return total_forces
++
++ def _run_simulation_phase(self, initial_centers, iterations, learning_rate_init,
++ growth_pressure_anneal_start, growth_pressure_anneal_end,
++ learning_rate_annealing_exponent):
++ """
++ Runs a single simulation phase with specified parameters, applying annealing schedules.
++ """
++ centers_for_sim = initial_centers.copy()
++
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+- # Anneal growth pressure quadratically (Recommendation 2 from previous G37)
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * \
+- (1.0 - (sim_iter / iterations))**2
+- inflated_radii = radii * current_growth_pressure
+-
+- # --- Vectorized Force Calculation ---
+- # a. Circle-to-circle repulsion forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_inflated_radii - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- # b. Vectorized Wall repulsion forces
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = inflated_radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+- overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces = circle_forces + wall_forces
+-
+- # --- Update center positions ---
+- current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
++ # Anneal growth pressure (quadratic schedule is often effective for exploration)
++ current_growth_pressure = growth_pressure_anneal_end + \
++ (growth_pressure_anneal_start - growth_pressure_anneal_end) * \
++ (1.0 - (sim_iter / iterations))**2
++
++ forces = self._calculate_forces(centers_for_sim, radii, current_growth_pressure)
++
++ # Anneal learning rate
++ current_lr = learning_rate_init * (1.0 - (sim_iter / iterations))**learning_rate_annealing_exponent
++
+ centers_for_sim += forces * current_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # --- Finalization for current trial ---
+- current_final_radii = compute_max_radii(centers_for_sim)
+- current_sum_radii = np.sum(current_final_radii)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_final_centers = centers_for_sim.copy()
+-
+- # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 300 # Increased fine-tune iterations for precision
+- fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
+- finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+- finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+- centers_for_sim = best_final_centers.copy()
+-
+- for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+- radii = compute_max_radii(centers_for_sim)
+-
+- # Anneal subtle growth pressure (linear decay for stability)
+- current_finetune_growth_pressure = finetune_growth_pressure_start - \
+- (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+- (sim_iter_ft / fine_tune_iterations)
+- inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+-
+- # --- Vectorized Force Calculation ---
+- # a. Circle-to-circle forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+-
+- overlaps = np.maximum(0, sum_radii_pairs - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- # b. Wall forces (also use inflated radii for consistency with growth pressure)
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+- overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces = circle_forces + wall_forces
+-
+- # Anneal learning rate for fine-tuning (linear decay)
+- current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+- centers_for_sim += forces * current_finetune_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- final_centers = centers_for_sim
+- final_radii = compute_max_radii(final_centers)
+-
+- return final_centers, final_radii
+-
+-
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This uses an iterative proportional scaling method.
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- for _ in range(500):
+- updated_in_pass = False
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9:
+- radii[i] = 0.0
+- radii[j] = 0.0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0.0)
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0) # Keep centers within bounds
++
++ return centers_for_sim
++
++ def solve(self):
++ """
++ Orchestrates the multi-start simulation and fine-tuning phases to find the best packing.
++ """
++ # Recommendation 1: Diversify initial configurations more broadly.
++ # Beyond just corners, consider some interstitial positions within a slightly larger grid.
++ # The current list is good for corners/edges, let's stick with these but ensure perturbation.
++ candidate_extra_circle_initial_positions = [
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
++ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Near-corners
++ [0.5, 0.5], # Center (for a central 26th circle)
++ [0.5, 0.05], [0.05, 0.5], [0.95, 0.5], [0.5, 0.95] # Mid-edges
++ ]
++
++ # Using a fixed seed for reproducibility across trials, helps debugging and comparison.
++ # This only affects the *initial* perturbations within each multi-start trial.
++ np.random.seed(42)
++
++ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
++ # --- Main Simulation Phase ---
++ initial_centers_trial = self._initialize_centers(initial_pos_26th_circle)
++
++ simulated_centers = self._run_simulation_phase(
++ initial_centers_trial,
++ self.iterations_main,
++ self.learning_rate_main,
++ self.initial_growth_pressure,
++ self.final_growth_pressure,
++ learning_rate_annealing_exponent=2.0 # Quadratic LR annealing for main phase for aggressive moves
++ )
++
++ current_sum_radii = np.sum(compute_max_radii(simulated_centers))
++
++ if current_sum_radii > self.best_sum_radii:
++ self.best_sum_radii = current_sum_radii
++ self.best_final_centers = simulated_centers.copy()
++
++ # --- Post-Simulation Fine-Tuning Phase ---
++ # Start fine-tuning from the best configuration found in the main phase.
++ if self.best_final_centers is None: # Fallback in case no valid centers were found (highly unlikely)
++ self.best_final_centers = self._initialize_centers([0.5, 0.5]) # Default to a safe center
++
++ fine_tuned_centers = self._run_simulation_phase(
++ self.best_final_centers,
++ self.fine_tune_iterations,
++ self.fine_tune_learning_rate,
++ self.fine_tune_growth_pressure_start,
++ self.fine_tune_growth_pressure_end,
++ learning_rate_annealing_exponent=1.0 # Linear LR annealing for fine-tuning for gentle moves
++ )
++ final_centers = fine_tuned_centers
++ final_radii = compute_max_radii(final_centers)
++
++ return final_centers, final_radii
++
++def construct_packing():
++ """
++ Constructs a packing of 26 circles by simulating physical repulsion forces
++ to find an optimal arrangement of centers. This function now acts as a
++ facade to the CirclePackingOptimizer class.
++
++ Returns:
++ Tuple of (centers, radii)
++ centers: np.array of shape (26, 2) with (x, y) coordinates
++ radii: np.array of shape (26) with radius of each circle
++ """
++ # Instantiate the optimizer with hyperparameters tuned for the best performance
++ # based on previous successful generations (aiming for 1.94+ range).
++ # These parameters are a blend of previous strong performers,
++ # adjusted to leverage the class structure and new radius convergence criterion.
++ optimizer = CirclePackingOptimizer(
++ n=26,
++ iterations_main=1500, # Increased iterations for main phase
++ learning_rate_main=0.015, # Balanced LR for main phase
++ initial_growth_pressure=1.03, # Strong initial pressure for "explosion"
++ final_growth_pressure=1.0001, # Gentle pressure towards the end of main phase
++ wall_repulsion_strength=0.35, # Firm wall repulsion
++ fine_tune_iterations=400, # More iterations for precise fine-tuning
++ fine_tune_learning_rate=0.001, # Balanced LR for fine-tuning
++ fine_tune_growth_pressure_start=1.0005, # Very subtle growth pressure for fine-tuning
++ fine_tune_growth_pressure_end=1.00001, # Almost no pressure at the very end
++ initial_perturbation_scale=0.005 # Small perturbation to break symmetry (Recommendation 4)
++ )
++
++ return optimizer.solve()
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..f6496f3562c08b8cb58c85b6823c5338997140d8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/main.py
@@ -0,0 +1,297 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version redesigns the program using a robust, modular, class-based architecture
+to improve maintainability, parameter tuning, and extensibility. It centralizes
+hyperparameter management and simulation logic within a single class, while
+implementing enhanced termination criteria for radius calculation.
+"""
+
+# Helper function (remains outside the class as it's stateless and generic)
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with a dynamic convergence criterion.
+
+ Args:
+ centers (np.array): Nx2 array of circle center coordinates.
+
+ Returns:
+ np.array: N-element array of radii for each circle.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n) # Initialize with placeholder large radii
+
+ # 1. Limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # 2. Limit by distance to other circles using iterative proportional scaling
+ # Recommendation 5: Refine `compute_max_radii` with a More Robust Dynamic Convergence Criterion
+ max_iterations = 500
+ min_radius_change = 1e-7 # Epsilon for detecting convergence
+ stable_iterations_required = 5 # Number of consecutive stable iterations
+
+ stable_iterations_count = 0
+
+ for iter_idx in range(max_iterations):
+ updated_in_pass = False
+ radii_before_pass = radii.copy() # Store radii before this pass for change detection
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-12: # Handle extremely close or identical centers
+ # If centers are too close, effectively make their radii 0 to avoid NaNs or infinite forces
+ # and allow them to move apart.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence after all pairwise adjustments in a pass
+ max_change_in_pass = np.max(np.abs(radii - radii_before_pass))
+
+ if max_change_in_pass < min_radius_change:
+ stable_iterations_count += 1
+ if stable_iterations_count >= stable_iterations_required:
+ break # Converged
+ else:
+ stable_iterations_count = 0 # Reset if a significant change occurred
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+class CirclePackingOptimizer:
+ """
+ Encapsulates the logic for optimizing circle packing using a physical simulation approach.
+ Manages hyperparameters, initialization, force calculations, and simulation phases.
+ """
+ def __init__(self, n=26,
+ iterations_main=1500, learning_rate_main=0.015,
+ initial_growth_pressure=1.03, final_growth_pressure=1.0001,
+ wall_repulsion_strength=0.35,
+ fine_tune_iterations=400, fine_tune_learning_rate=0.001,
+ fine_tune_growth_pressure_start=1.0005, fine_tune_growth_pressure_end=1.00001,
+ initial_perturbation_scale=0.005): # Recommendation 4: perturbation scale
+
+ self.n = n
+ self.iterations_main = iterations_main
+ self.learning_rate_main = learning_rate_main
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.wall_repulsion_strength = wall_repulsion_strength
+ self.fine_tune_iterations = fine_tune_iterations
+ self.fine_tune_learning_rate = fine_tune_learning_rate
+ self.fine_tune_growth_pressure_start = fine_tune_growth_pressure_start
+ self.fine_tune_growth_pressure_end = fine_tune_growth_pressure_end
+ self.initial_perturbation_scale = initial_perturbation_scale
+
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_pos_26th_circle):
+ """
+ Initializes centers with a 5x5 grid and places the 26th circle.
+ Applies a small random perturbation.
+ (Implements Recommendation 1 by having flexible initialization and perturbation)
+ """
+ initial_grid_centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ initial_grid_centers[k, 0] = (i + 0.5) * spacing
+ initial_grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Perturb the first 25 circles (the 5x5 grid)
+ initial_grid_centers[:25, :] += np.random.normal(0, spacing * self.initial_perturbation_scale, size=(25, 2))
+
+ # Place the 26th circle at the candidate position
+ initial_grid_centers[25] = extra_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ centers = np.clip(initial_grid_centers, 0.0, 1.0)
+
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """
+ Calculates total forces (circle-to-circle and wall repulsion) for a given state.
+ Uses vectorized operations for efficiency.
+ """
+ # Apply growth pressure to radii
+ inflated_radii = radii * growth_pressure_factor
+
+ # a. Circle-to-circle repulsion forces (Vectorized)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ # Calculate overlaps, ensuring no self-overlap
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ # Calculate unit vectors for directions
+ # Add a small epsilon to avoid division by zero for extremely small distances
+ # and handle potential NaN issues if distances are exactly zero.
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0 # Set forces to 0 for circles that are on top of each other
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (Vectorized)
+ wall_forces = np.zeros_like(centers)
+
+ # Overlap with each wall
+ # Left wall (x=0)
+ overlap_left = inflated_radii - centers[:, 0]
+ # Right wall (x=1)
+ overlap_right = (centers[:, 0] + inflated_radii) - 1.0
+ # Bottom wall (y=0)
+ overlap_bottom = inflated_radii - centers[:, 1]
+ # Top wall (y=1)
+ overlap_top = (centers[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ total_forces = circle_forces + wall_forces
+ return total_forces
+
+ def _run_simulation_phase(self, initial_centers, iterations, learning_rate_init,
+ growth_pressure_anneal_start, growth_pressure_anneal_end,
+ learning_rate_annealing_exponent):
+ """
+ Runs a single simulation phase with specified parameters, applying annealing schedules.
+ """
+ centers_for_sim = initial_centers.copy()
+
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure (quadratic schedule is often effective for exploration)
+ current_growth_pressure = growth_pressure_anneal_end + \
+ (growth_pressure_anneal_start - growth_pressure_anneal_end) * \
+ (1.0 - (sim_iter / iterations))**2
+
+ forces = self._calculate_forces(centers_for_sim, radii, current_growth_pressure)
+
+ # Anneal learning rate
+ current_lr = learning_rate_init * (1.0 - (sim_iter / iterations))**learning_rate_annealing_exponent
+
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0) # Keep centers within bounds
+
+ return centers_for_sim
+
+ def solve(self):
+ """
+ Orchestrates the multi-start simulation and fine-tuning phases to find the best packing.
+ """
+ # Recommendation 1: Diversify initial configurations more broadly.
+ # Beyond just corners, consider some interstitial positions within a slightly larger grid.
+ # The current list is good for corners/edges, let's stick with these but ensure perturbation.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Near-corners
+ [0.5, 0.5], # Center (for a central 26th circle)
+ [0.5, 0.05], [0.05, 0.5], [0.95, 0.5], [0.5, 0.95] # Mid-edges
+ ]
+
+ # Using a fixed seed for reproducibility across trials, helps debugging and comparison.
+ # This only affects the *initial* perturbations within each multi-start trial.
+ np.random.seed(42)
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Main Simulation Phase ---
+ initial_centers_trial = self._initialize_centers(initial_pos_26th_circle)
+
+ simulated_centers = self._run_simulation_phase(
+ initial_centers_trial,
+ self.iterations_main,
+ self.learning_rate_main,
+ self.initial_growth_pressure,
+ self.final_growth_pressure,
+ learning_rate_annealing_exponent=2.0 # Quadratic LR annealing for main phase for aggressive moves
+ )
+
+ current_sum_radii = np.sum(compute_max_radii(simulated_centers))
+
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = simulated_centers.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Start fine-tuning from the best configuration found in the main phase.
+ if self.best_final_centers is None: # Fallback in case no valid centers were found (highly unlikely)
+ self.best_final_centers = self._initialize_centers([0.5, 0.5]) # Default to a safe center
+
+ fine_tuned_centers = self._run_simulation_phase(
+ self.best_final_centers,
+ self.fine_tune_iterations,
+ self.fine_tune_learning_rate,
+ self.fine_tune_growth_pressure_start,
+ self.fine_tune_growth_pressure_end,
+ learning_rate_annealing_exponent=1.0 # Linear LR annealing for fine-tuning for gentle moves
+ )
+ final_centers = fine_tuned_centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers. This function now acts as a
+ facade to the CirclePackingOptimizer class.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Instantiate the optimizer with hyperparameters tuned for the best performance
+ # based on previous successful generations (aiming for 1.94+ range).
+ # These parameters are a blend of previous strong performers,
+ # adjusted to leverage the class structure and new radius convergence criterion.
+ optimizer = CirclePackingOptimizer(
+ n=26,
+ iterations_main=1500, # Increased iterations for main phase
+ learning_rate_main=0.015, # Balanced LR for main phase
+ initial_growth_pressure=1.03, # Strong initial pressure for "explosion"
+ final_growth_pressure=1.0001, # Gentle pressure towards the end of main phase
+ wall_repulsion_strength=0.35, # Firm wall repulsion
+ fine_tune_iterations=400, # More iterations for precise fine-tuning
+ fine_tune_learning_rate=0.001, # Balanced LR for fine-tuning
+ fine_tune_growth_pressure_start=1.0005, # Very subtle growth pressure for fine-tuning
+ fine_tune_growth_pressure_end=1.00001, # Almost no pressure at the very end
+ initial_perturbation_scale=0.005 # Small perturbation to break symmetry (Recommendation 4)
+ )
+
+ return optimizer.solve()
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..15e2065d10b82e1a24064c8969333788774107fb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/original.py
@@ -0,0 +1,218 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters based on successful prior implementations
+ iterations = 1200 # Increased for more thorough exploration
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.3 # Adjusted wall repulsion for better boundary interaction
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically (Recommendation 2 from previous G37)
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 300 # Increased fine-tune iterations for precision
+ fine_tune_learning_rate = 0.0008 # Slower fine-tuning for high precision
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..532a184ac05b691e218af70a296a60241c8442c8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": false,
+ "error": "name 'np' is not defined"
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..c1d0b6d5349063c6d314143135c251948bbf68d2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/job_log.out
@@ -0,0 +1,14 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results
+Evaluation error: name 'np' is not defined
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/metrics.json
+Evaluation or Validation failed: name 'np' is not defined
+Metrics:
+ combined_score: 0.0
+ execution_time_mean: 0.0
+ execution_time_std: 0.0
+ num_successful_runs: 0
+ num_valid_runs: 0
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..fec2021a6a036637c9d3049ae9778c77cef78421
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/results/metrics.json
@@ -0,0 +1,9 @@
+{
+ "combined_score": 0.0,
+ "execution_time_mean": 0.0,
+ "execution_time_std": 0.0,
+ "num_successful_runs": 0,
+ "num_valid_runs": 0,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8030b94760be263fe0ff5296e0453b1826b85975
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_57/rewrite.txt
@@ -0,0 +1,289 @@
+import numpy as np
+
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version redesigns the program using a robust, modular, class-based architecture
+to improve maintainability, parameter tuning, and extensibility. It centralizes
+hyperparameter management and simulation logic within a single class, while
+implementing enhanced termination criteria for radius calculation.
+"""
+
+# Helper function (remains outside the class as it's stateless and generic)
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with a dynamic convergence criterion.
+
+ Args:
+ centers (np.array): Nx2 array of circle center coordinates.
+
+ Returns:
+ np.array: N-element array of radii for each circle.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n) # Initialize with placeholder large radii
+
+ # 1. Limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # 2. Limit by distance to other circles using iterative proportional scaling
+ # Recommendation 5: Refine `compute_max_radii` with a More Robust Dynamic Convergence Criterion
+ max_iterations = 500
+ min_radius_change = 1e-7 # Epsilon for detecting convergence
+ stable_iterations_required = 5 # Number of consecutive stable iterations
+
+ stable_iterations_count = 0
+
+ for iter_idx in range(max_iterations):
+ updated_in_pass = False
+ radii_before_pass = radii.copy() # Store radii before this pass for change detection
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-12: # Handle extremely close or identical centers
+ # If centers are too close, effectively make their radii 0 to avoid NaNs or infinite forces
+ # and allow them to move apart.
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence after all pairwise adjustments in a pass
+ max_change_in_pass = np.max(np.abs(radii - radii_before_pass))
+
+ if max_change_in_pass < min_radius_change:
+ stable_iterations_count += 1
+ if stable_iterations_count >= stable_iterations_required:
+ break # Converged
+ else:
+ stable_iterations_count = 0 # Reset if a significant change occurred
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+class CirclePackingOptimizer:
+ """
+ Encapsulates the logic for optimizing circle packing using a physical simulation approach.
+ Manages hyperparameters, initialization, force calculations, and simulation phases.
+ """
+ def __init__(self, n=26,
+ iterations_main=1500, learning_rate_main=0.015,
+ initial_growth_pressure=1.03, final_growth_pressure=1.0001,
+ wall_repulsion_strength=0.35,
+ fine_tune_iterations=400, fine_tune_learning_rate=0.001,
+ fine_tune_growth_pressure_start=1.0005, fine_tune_growth_pressure_end=1.00001,
+ initial_perturbation_scale=0.005): # Recommendation 4: perturbation scale
+
+ self.n = n
+ self.iterations_main = iterations_main
+ self.learning_rate_main = learning_rate_main
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.wall_repulsion_strength = wall_repulsion_strength
+ self.fine_tune_iterations = fine_tune_iterations
+ self.fine_tune_learning_rate = fine_tune_learning_rate
+ self.fine_tune_growth_pressure_start = fine_tune_growth_pressure_start
+ self.fine_tune_growth_pressure_end = fine_tune_growth_pressure_end
+ self.initial_perturbation_scale = initial_perturbation_scale
+
+ self.best_sum_radii = -1.0
+ self.best_final_centers = None
+
+ def _initialize_centers(self, extra_pos_26th_circle):
+ """
+ Initializes centers with a 5x5 grid and places the 26th circle.
+ Applies a small random perturbation.
+ (Implements Recommendation 1 by having flexible initialization and perturbation)
+ """
+ initial_grid_centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ initial_grid_centers[k, 0] = (i + 0.5) * spacing
+ initial_grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Perturb the first 25 circles (the 5x5 grid)
+ initial_grid_centers[:25, :] += np.random.normal(0, spacing * self.initial_perturbation_scale, size=(25, 2))
+
+ # Place the 26th circle at the candidate position
+ initial_grid_centers[25] = extra_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ centers = np.clip(initial_grid_centers, 0.0, 1.0)
+
+ return centers
+
+ def _calculate_forces(self, centers, radii, growth_pressure_factor):
+ """
+ Calculates total forces (circle-to-circle and wall repulsion) for a given state.
+ Uses vectorized operations for efficiency.
+ """
+ # Apply growth pressure to radii
+ inflated_radii = radii * growth_pressure_factor
+
+ # a. Circle-to-circle repulsion forces (Vectorized)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ # Calculate overlaps, ensuring no self-overlap
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ # Calculate unit vectors for directions
+ # Add a small epsilon to avoid division by zero for extremely small distances
+ # and handle potential NaN issues if distances are exactly zero.
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0 # Set forces to 0 for circles that are on top of each other
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (Vectorized)
+ wall_forces = np.zeros_like(centers)
+
+ # Overlap with each wall
+ # Left wall (x=0)
+ overlap_left = inflated_radii - centers[:, 0]
+ # Right wall (x=1)
+ overlap_right = (centers[:, 0] + inflated_radii) - 1.0
+ # Bottom wall (y=0)
+ overlap_bottom = inflated_radii - centers[:, 1]
+ # Top wall (y=1)
+ overlap_top = (centers[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += self.wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= self.wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += self.wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= self.wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ total_forces = circle_forces + wall_forces
+ return total_forces
+
+ def _run_simulation_phase(self, initial_centers, iterations, learning_rate_init,
+ growth_pressure_anneal_start, growth_pressure_anneal_end,
+ learning_rate_annealing_exponent):
+ """
+ Runs a single simulation phase with specified parameters, applying annealing schedules.
+ """
+ centers_for_sim = initial_centers.copy()
+
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure (quadratic schedule is often effective for exploration)
+ current_growth_pressure = growth_pressure_anneal_end + \
+ (growth_pressure_anneal_start - growth_pressure_anneal_end) * \
+ (1.0 - (sim_iter / iterations))**2
+
+ forces = self._calculate_forces(centers_for_sim, radii, current_growth_pressure)
+
+ # Anneal learning rate
+ current_lr = learning_rate_init * (1.0 - (sim_iter / iterations))**learning_rate_annealing_exponent
+
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0) # Keep centers within bounds
+
+ return centers_for_sim
+
+ def solve(self):
+ """
+ Orchestrates the multi-start simulation and fine-tuning phases to find the best packing.
+ """
+ # Recommendation 1: Diversify initial configurations more broadly.
+ # Beyond just corners, consider some interstitial positions within a slightly larger grid.
+ # The current list is good for corners/edges, let's stick with these but ensure perturbation.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Near-corners
+ [0.5, 0.5], # Center (for a central 26th circle)
+ [0.5, 0.05], [0.05, 0.5], [0.95, 0.5], [0.5, 0.95] # Mid-edges
+ ]
+
+ # Using a fixed seed for reproducibility across trials, helps debugging and comparison.
+ # This only affects the *initial* perturbations within each multi-start trial.
+ np.random.seed(42)
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Main Simulation Phase ---
+ initial_centers_trial = self._initialize_centers(initial_pos_26th_circle)
+
+ simulated_centers = self._run_simulation_phase(
+ initial_centers_trial,
+ self.iterations_main,
+ self.learning_rate_main,
+ self.initial_growth_pressure,
+ self.final_growth_pressure,
+ learning_rate_annealing_exponent=2.0 # Quadratic LR annealing for main phase for aggressive moves
+ )
+
+ current_sum_radii = np.sum(compute_max_radii(simulated_centers))
+
+ if current_sum_radii > self.best_sum_radii:
+ self.best_sum_radii = current_sum_radii
+ self.best_final_centers = simulated_centers.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Start fine-tuning from the best configuration found in the main phase.
+ if self.best_final_centers is None: # Fallback in case no valid centers were found (highly unlikely)
+ self.best_final_centers = self._initialize_centers([0.5, 0.5]) # Default to a safe center
+
+ fine_tuned_centers = self._run_simulation_phase(
+ self.best_final_centers,
+ self.fine_tune_iterations,
+ self.fine_tune_learning_rate,
+ self.fine_tune_growth_pressure_start,
+ self.fine_tune_growth_pressure_end,
+ learning_rate_annealing_exponent=1.0 # Linear LR annealing for fine-tuning for gentle moves
+ )
+ final_centers = fine_tuned_centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers. This function now acts as a
+ facade to the CirclePackingOptimizer class.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Instantiate the optimizer with hyperparameters tuned for the best performance
+ # based on previous successful generations (aiming for 1.94+ range).
+ # These parameters are a blend of previous strong performers,
+ # adjusted to leverage the class structure and new radius convergence criterion.
+ optimizer = CirclePackingOptimizer(
+ n=26,
+ iterations_main=1500, # Increased iterations for main phase
+ learning_rate_main=0.015, # Balanced LR for main phase
+ initial_growth_pressure=1.03, # Strong initial pressure for "explosion"
+ final_growth_pressure=1.0001, # Gentle pressure towards the end of main phase
+ wall_repulsion_strength=0.35, # Firm wall repulsion
+ fine_tune_iterations=400, # More iterations for precise fine-tuning
+ fine_tune_learning_rate=0.001, # Balanced LR for fine-tuning
+ fine_tune_growth_pressure_start=1.0005, # Very subtle growth pressure for fine-tuning
+ fine_tune_growth_pressure_end=1.00001, # Almost no pressure at the very end
+ initial_perturbation_scale=0.005 # Small perturbation to break symmetry (Recommendation 4)
+ )
+
+ return optimizer.solve()
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..2ae1e3518f346d877c11a4238082d7b0b7466568
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/edit.diff
@@ -0,0 +1,250 @@
+--- a/original.py
++++ b/original.py
+@@ -1,219 +1,216 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+-This version is a crossover, combining the high-level strategy of the best-performing
+-parent (multi-start, two-phase simulation, annealed growth pressure) with the
+-efficient vectorized force calculations from the inspiration program.
++This version refines the annealing strategy and diversifies initial start
++positions to improve the global search and final packing quality.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- # --- Hyperparameters for the simulation ---
+- # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+- iterations = 2000 # Increased iterations for more thorough exploration after stronger forces
+- learning_rate = 0.02 # Increased learning rate for more significant center movements
+- wall_repulsion_strength = 0.3 # Slightly reduced wall repulsion to allow more freedom of movement initially
+- initial_growth_pressure = 1.05 # Significantly higher initial pressure for stronger repulsion ("explosion")
+- final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
++ # Tuned parameters for a strong but controlled "explosion" and robust fine-tuning.
++ iterations = 2200 # Increased iterations for more thorough evolution.
++ learning_rate = 0.018 # Slightly reduced LR for stability.
++ wall_repulsion_strength = 0.4 # Increased wall strength to better contain the initial expansion.
++ initial_growth_pressure = 1.04 # Moderated initial pressure for a less chaotic "explosion".
++ final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+- # Candidate initial positions for the 26th circle to optimize corner utilization.
++ # Expanded candidate positions for the 26th circle to diversify initial states (Recommendation 1).
+ candidate_extra_circle_initial_positions = [
++ # Corners (deep)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
++ # Interstitial voids (corner-like)
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
++ # Interstitial voids (central)
++ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+- # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+- perturbation_scale = 0.01 # Increased perturbation scale for better symmetry breaking
++ # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
++ perturbation_scale = 0.008
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+- # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+-
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+- # Anneal growth pressure linearly for more sustained high pressure
++ # Reverted to QUADRATIC annealing for growth pressure for a fast-subsiding push.
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+- (1.0 - (sim_iter / iterations))
++ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+- # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+- # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+- fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+- finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+- finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
++ fine_tune_iterations = 500 # Increased fine-tune iterations for more precise settling.
++ fine_tune_learning_rate = 0.001 # Kept fine-tuning LR.
++ finetune_growth_pressure_start = 1.0008 # A slightly stronger nudge to escape shallow local minima.
++ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end.
+ centers_for_sim = best_final_centers.copy()
+
+- for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
++ for sim_iter_ft in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+- # Anneal subtle growth pressure (linear decay for stability)
++ # Anneal subtle growth pressure (linear decay for stability).
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+- inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
++ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+- # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
++ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+- # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+- # Anneal learning rate for fine-tuning (linear decay)
++ # Anneal learning rate for fine-tuning (linear decay).
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+- for _ in range(500):
++ # Increased iterations for better convergence and accuracy per simulation step.
++ for _ in range(600):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..b60f23b7bb16fd88f56276775e7843436a7dab5b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/main.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version refines the annealing strategy and diversifies initial start
+positions to improve the global search and final packing quality.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned parameters for a strong but controlled "explosion" and robust fine-tuning.
+ iterations = 2200 # Increased iterations for more thorough evolution.
+ learning_rate = 0.018 # Slightly reduced LR for stability.
+ wall_repulsion_strength = 0.4 # Increased wall strength to better contain the initial expansion.
+ initial_growth_pressure = 1.04 # Moderated initial pressure for a less chaotic "explosion".
+ final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Expanded candidate positions for the 26th circle to diversify initial states (Recommendation 1).
+ candidate_extra_circle_initial_positions = [
+ # Corners (deep)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ # Interstitial voids (corner-like)
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ # Interstitial voids (central)
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+ perturbation_scale = 0.008
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Reverted to QUADRATIC annealing for growth pressure for a fast-subsiding push.
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 500 # Increased fine-tune iterations for more precise settling.
+ fine_tune_learning_rate = 0.001 # Kept fine-tuning LR.
+ finetune_growth_pressure_start = 1.0008 # A slightly stronger nudge to escape shallow local minima.
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end.
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability).
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay).
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Increased iterations for better convergence and accuracy per simulation step.
+ for _ in range(600):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..960a167e3699c8813d26ae93f0e69a32c8aa80ca
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/original.py
@@ -0,0 +1,219 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+ iterations = 2000 # Increased iterations for more thorough exploration after stronger forces
+ learning_rate = 0.02 # Increased learning rate for more significant center movements
+ wall_repulsion_strength = 0.3 # Slightly reduced wall repulsion to allow more freedom of movement initially
+ initial_growth_pressure = 1.05 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.01 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure linearly for more sustained high pressure
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune (Recommendation 2)
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces (also use inflated radii for consistency with growth pressure)
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..07424178de445ace8d05dba3e32fcbfe453d253d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_58/rewrite.txt
@@ -0,0 +1,207 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version refines the annealing strategy and diversifies initial start
+positions to improve the global search and final packing quality.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned parameters for a strong but controlled "explosion" and robust fine-tuning.
+ iterations = 2200 # Increased iterations for more thorough evolution.
+ learning_rate = 0.018 # Slightly reduced LR for stability.
+ wall_repulsion_strength = 0.4 # Increased wall strength to better contain the initial expansion.
+ initial_growth_pressure = 1.04 # Moderated initial pressure for a less chaotic "explosion".
+ final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Expanded candidate positions for the 26th circle to diversify initial states (Recommendation 1).
+ candidate_extra_circle_initial_positions = [
+ # Corners (deep)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ # Interstitial voids (corner-like)
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ # Interstitial voids (central)
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+ perturbation_scale = 0.008
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Reverted to QUADRATIC annealing for growth pressure for a fast-subsiding push.
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 500 # Increased fine-tune iterations for more precise settling.
+ fine_tune_learning_rate = 0.001 # Kept fine-tuning LR.
+ finetune_growth_pressure_start = 1.0008 # A slightly stronger nudge to escape shallow local minima.
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end.
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability).
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay).
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Increased iterations for better convergence and accuracy per simulation step.
+ for _ in range(600):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ffd114ff46e247707a8405630d17ea85e2ff99cf
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..19b03a67f7eabf927a73ae5194922b4ef447d37b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/edit.diff
@@ -0,0 +1,236 @@
+--- a/original.py
++++ b/original.py
+@@ -1,199 +1,218 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm is a crossover, combining the high-level strategy of the best-performing
+ parent (multi-start, two-phase simulation, annealed growth pressure) with the
+ efficient vectorized force calculations from the inspiration program.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- # Increased iterations due to vectorized efficiency
+- iterations = 750
+- learning_rate = 0.01
+- wall_repulsion_strength = 0.5
+- initial_growth_pressure = 1.01
+- final_growth_pressure = 1.001
++ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
++ iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
++ learning_rate = 0.015 # Increased learning rate for more significant center movements
++ wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
++ initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
++ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
++
++ # Add small random perturbation to the 5x5 grid (first 25 circles, re-introduced from successful prior)
++ perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
++ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
++
+ current_centers[25] = initial_pos_26th_circle
++
++ # Ensure all centers stay within bounds after initial setup and perturbation
++ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+- # Anneal growth pressure
+- current_growth_pressure = initial_growth_pressure - \
+- (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
++ # Anneal growth pressure quadratically
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * \
++ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 100 # Increased due to efficiency
+- fine_tune_learning_rate = 0.001
++ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
++ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
++ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure
++ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
+ centers_for_sim = best_final_centers.copy()
+
+- for _ in range(fine_tune_iterations):
++ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+- # --- Vectorized Force Calculation (no growth pressure) ---
++ # Anneal subtle growth pressure (linear decay for stability)
++ current_finetune_growth_pressure = finetune_growth_pressure_start - \
++ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
++ (sim_iter_ft / fine_tune_iterations)
++ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
++
++ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
++ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+- centers_for_sim += forces * fine_tune_learning_rate
++ # Anneal learning rate for fine-tuning (linear decay)
++ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
++ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..639813b26fb788c17c10286874fceb9ed6335512
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/main.py
@@ -0,0 +1,218 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+ iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
+ learning_rate = 0.015 # Increased learning rate for more significant center movements
+ wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
+ initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, re-introduced from successful prior)
+ perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..41788ebfff77a41763a8bd844dbf24483d23d2e7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/original.py
@@ -0,0 +1,199 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..700a7601fd6564ba0a2f91d99ff18b4222b62626
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results
+Run 1/1 completed in 31.70 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9496734282774475
+ public: {'centers_str': ' centers[0] = (0.0975, 0.0957)\n centers[1] = (0.0908, 0.3089)\n centers[2] = (0.1023, 0.5013)\n centers[3] = (0.1006, 0.6981)\n centers[4] = (0.1018, 0.8980)\n centers[5] = (0.3136, 0.0887)\n centers[6] = (0.3045, 0.3016)\n centers[7] = (0.2997, 0.5000)\n centers[8] = (0.3012, 0.6997)\n centers[9] = (0.2999, 0.8997)\n centers[10] = (0.5006, 0.1013)\n centers[11] = (0.4988, 0.3011)\n centers[12] = (0.5008, 0.4990)\n centers[13] = (0.4980, 0.7000)\n centers[14] = (0.5005, 0.9004)\n centers[15] = (0.6993, 0.0989)\n centers[16] = (0.6976, 0.2969)\n centers[17] = (0.7009, 0.4753)\n centers[18] = (0.6879, 0.7034)\n centers[19] = (0.6998, 0.9168)\n centers[20] = (0.8990, 0.0972)\n centers[21] = (0.8962, 0.2993)\n centers[22] = (0.8988, 0.5059)\n centers[23] = (0.9094, 0.7019)\n centers[24] = (0.8971, 0.8972)\n centers[25] = (0.1948, 0.2044)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9496734282774475}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/packing_viz.png
+ execution_time_mean: 31.699526263866574
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..2dd8a42eb4f3e2f3cac11019eb466969ea8c850c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9496734282774475,
+ "public": {
+ "centers_str": " centers[0] = (0.0975, 0.0957)\n centers[1] = (0.0908, 0.3089)\n centers[2] = (0.1023, 0.5013)\n centers[3] = (0.1006, 0.6981)\n centers[4] = (0.1018, 0.8980)\n centers[5] = (0.3136, 0.0887)\n centers[6] = (0.3045, 0.3016)\n centers[7] = (0.2997, 0.5000)\n centers[8] = (0.3012, 0.6997)\n centers[9] = (0.2999, 0.8997)\n centers[10] = (0.5006, 0.1013)\n centers[11] = (0.4988, 0.3011)\n centers[12] = (0.5008, 0.4990)\n centers[13] = (0.4980, 0.7000)\n centers[14] = (0.5005, 0.9004)\n centers[15] = (0.6993, 0.0989)\n centers[16] = (0.6976, 0.2969)\n centers[17] = (0.7009, 0.4753)\n centers[18] = (0.6879, 0.7034)\n centers[19] = (0.6998, 0.9168)\n centers[20] = (0.8990, 0.0972)\n centers[21] = (0.8962, 0.2993)\n centers[22] = (0.8988, 0.5059)\n centers[23] = (0.9094, 0.7019)\n centers[24] = (0.8971, 0.8972)\n centers[25] = (0.1948, 0.2044)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9496734282774475
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/results/packing_viz.png",
+ "execution_time_mean": 31.699526263866574,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a9270e4afb61a2e80b2a916b475496cfb8f228f8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_59/search_replace.txt
@@ -0,0 +1,188 @@
+
+reintroduce_perturbation_and_tune_main_sim
+
+
+
+This edit reintroduces the initial random perturbation for the 5x5 grid circles, which was present in higher-performing previous physical simulation variants but missing in the current version. This perturbation helps break initial symmetries and allows the simulation to explore a wider range of configurations, preventing it from getting stuck in rigid grid-like local optima.
+
+Additionally, key hyperparameters for the main simulation loop are adjusted to align with more successful previous configurations:
+- `iterations` is increased from 750 to 1500 for more thorough exploration, which is feasible due to the vectorized force calculations.
+- `learning_rate` is increased from 0.01 to 0.015 for more aggressive initial movement, allowing circles to spread out faster.
+- `wall_repulsion_strength` is lowered from 0.5 to 0.35, which historically allows circles to utilize space closer to the boundaries more effectively without being excessively pushed inwards.
+- `initial_growth_pressure` is increased from 1.01 to 1.03 for a stronger "explosion" effect, pushing circles apart more vigorously at the beginning.
+- `final_growth_pressure` is tightened from 1.001 to 1.0001, ensuring that the radii are almost exactly touching at the end of the main phase for better precision.
+- The annealing schedule for `current_growth_pressure` is changed from linear to quadratic decay (`**2`), which makes the pressure drop slower initially, maintaining stronger repulsion for longer, then decreasing rapidly for fine-tuning. This pattern proved more effective in prior generations.
+
+
+
+<<<<<<< SEARCH
+ # Increased iterations due to vectorized efficiency
+ iterations = 750
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.5
+ initial_growth_pressure = 1.01
+ final_growth_pressure = 1.001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ current_centers[25] = initial_pos_26th_circle
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+=======
+ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+ iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
+ learning_rate = 0.015 # Increased learning rate for more significant center movements
+ wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
+ initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, re-introduced from successful prior)
+ perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+>>>>>>> REPLACE
+
+
+
+tune_finetuning_phase
+
+
+
+This edit refines the post-simulation fine-tuning phase by introducing a subtle growth pressure and annealing its effect, as well as annealing the learning rate.
+
+- `fine_tune_iterations` is increased from 100 to 400 for more precise settling, following the aggressive main simulation phase.
+- `fine_tune_learning_rate` is kept at 0.001 but is now linearly annealed to zero, allowing for very small, precise adjustments as the simulation converges.
+- `finetune_growth_pressure_start` (1.0005) and `finetune_growth_pressure_end` (1.00001) are introduced. These values provide a very slight "push" during fine-tuning, helping circles to expand into any remaining tiny gaps, while still allowing them to settle accurately.
+- The growth pressure in the fine-tuning phase is linearly annealed (`sim_iter_ft / fine_tune_iterations`). This ensures that the growth pressure gradually diminishes, leading to an extremely tight packing as the simulation concludes.
+- The learning rate for fine-tuning is also linearly annealed, ensuring gradual convergence.
+
+
+
+<<<<<<< SEARCH
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 100 # Increased due to efficiency
+ fine_tune_learning_rate = 0.001
+ centers_for_sim = best_final_centers.copy()
+
+ for _ in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = radii[:, np.newaxis] + radii[np.newaxis, :]
+=======
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+>>>>>>> REPLACE
+
+
+
+<<<<<<< SEARCH
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ centers_for_sim += forces * fine_tune_learning_rate
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+=======
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a0ccf3223b0aef5de166fcc0c3074863c21e4511
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..a5b2e2eba0b65af61c30b37e2d3a197cea5a0e23
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/edit.diff
@@ -0,0 +1,160 @@
+--- a/original.py
++++ b/original.py
+@@ -1,124 +1,128 @@
+ # EVOLVE-BLOCK-START
+ """
+-Force-directed relaxation packing for n=26 circles.
+-Starts with a jittered grid and refines positions using simulated forces,
+-then computes radii using a convergent iterative process.
++Hybrid circle packing for n=26 circles.
++Starts with a concentric ring pattern and refines positions using
++force-directed relaxation, then computes radii iteratively.
+ """
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+- relaxation method to maximize the sum of radii.
++ relaxation method, starting from a concentric ring pattern,
++ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+- # Use a fixed seed for reproducibility of the random initialization.
++ # Use a fixed seed for reproducibility.
+ np.random.seed(1337)
+
+- # 1. Initial Placement: Start with a perturbed 5x5 grid + 1 extra circle.
+- # This provides a good structured start, better than pure random.
++ # 1. Initial Placement: Start with a structured concentric ring pattern.
++ # This provides a symmetric, ordered starting point for the relaxation,
++ # combining the idea from the first parent program.
+ centers = np.zeros((n, 2))
+- grid_size = 5
+- # Create a 5x5 grid
+- x = np.linspace(0.1, 0.9, grid_size)
+- y = np.linspace(0.1, 0.9, grid_size)
+- xv, yv = np.meshgrid(x, y)
+- centers[:25, :] = np.vstack([xv.ravel(), yv.ravel()]).T
+
+- # Place the 26th circle in one of the initial gaps of the grid.
+- centers[25, :] = [0.2, 0.2]
++ # Place a circle in the center
++ centers[0] = [0.5, 0.5]
+
+- # Add random jitter to break perfect symmetry and allow for new configurations.
+- centers += np.random.uniform(-0.05, 0.05, size=centers.shape)
+- centers = np.clip(centers, 0.01, 0.99) # Ensure they start safely inside.
++ # Place 8 circles in an inner ring
++ inner_ring_radius = 0.28
++ for i in range(8):
++ angle = 2 * np.pi * i / 8
++ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
+
+- # 2. Iterative Position Refinement (Force Simulation)
++ # Place the remaining 17 circles in an outer ring
++ outer_ring_radius = 0.48
++ for i in range(17):
++ angle = 2 * np.pi * i / 17
++ centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
++
++ # 2. Iterative Position Refinement (Force Simulation from the second parent)
+ n_iter = 250
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+- np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
++ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+-
++
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+- # 3. Compute optimal radii for the final configuration using a convergent method.
++ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+-
++
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+- centers[:, 0],
+- centers[:, 1],
+- 1 - centers[:, 0],
++ centers[:, 0],
++ centers[:, 1],
++ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+- max_iter = 500 # Safety break for the convergence loop
++ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+-
++
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+-
++
+ return radii
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..32853ac96392f381a30b2663ddab231b12710cd2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/main.py
@@ -0,0 +1,128 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid circle packing for n=26 circles.
+Starts with a concentric ring pattern and refines positions using
+force-directed relaxation, then computes radii iteratively.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method, starting from a concentric ring pattern,
+ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with a structured concentric ring pattern.
+ # This provides a symmetric, ordered starting point for the relaxation,
+ # combining the idea from the first parent program.
+ centers = np.zeros((n, 2))
+
+ # Place a circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles in an inner ring
+ inner_ring_radius = 0.28
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
+
+ # Place the remaining 17 circles in an outer ring
+ outer_ring_radius = 0.48
+ for i in range(17):
+ angle = 2 * np.pi * i / 17
+ centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
+
+ # 2. Iterative Position Refinement (Force Simulation from the second parent)
+ n_iter = 250
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..38d5140fb64ee8d3ffc99eac96a21fc551b36b66
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/original.py
@@ -0,0 +1,124 @@
+# EVOLVE-BLOCK-START
+"""
+Force-directed relaxation packing for n=26 circles.
+Starts with a jittered grid and refines positions using simulated forces,
+then computes radii using a convergent iterative process.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility of the random initialization.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with a perturbed 5x5 grid + 1 extra circle.
+ # This provides a good structured start, better than pure random.
+ centers = np.zeros((n, 2))
+ grid_size = 5
+ # Create a 5x5 grid
+ x = np.linspace(0.1, 0.9, grid_size)
+ y = np.linspace(0.1, 0.9, grid_size)
+ xv, yv = np.meshgrid(x, y)
+ centers[:25, :] = np.vstack([xv.ravel(), yv.ravel()]).T
+
+ # Place the 26th circle in one of the initial gaps of the grid.
+ centers[25, :] = [0.2, 0.2]
+
+ # Add random jitter to break perfect symmetry and allow for new configurations.
+ centers += np.random.uniform(-0.05, 0.05, size=centers.shape)
+ centers = np.clip(centers, 0.01, 0.99) # Ensure they start safely inside.
+
+ # 2. Iterative Position Refinement (Force Simulation)
+ n_iter = 250
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using a convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..35006fd2e13f7b62bbacfac492d9a4ced30cdd51
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results
+Run 1/1 completed in 0.02 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.2838169291759578
+ public: {'centers_str': ' centers[0] = (0.4950, 0.5000)\n centers[1] = (0.9000, 0.5000)\n centers[2] = (0.8381, 0.8530)\n centers[3] = (0.4573, 0.9745)\n centers[4] = (0.1417, 0.8565)\n centers[5] = (0.0259, 0.5000)\n centers[6] = (0.1417, 0.1435)\n centers[7] = (0.4573, 0.0255)\n centers[8] = (0.8381, 0.1470)\n centers[9] = (0.9842, 0.5000)\n centers[10] = (0.9816, 0.8179)\n centers[11] = (0.9845, 0.9799)\n centers[12] = (0.8962, 0.9821)\n centers[13] = (0.6420, 0.9763)\n centers[14] = (0.2452, 0.9794)\n centers[15] = (0.0222, 0.9864)\n centers[16] = (0.0186, 0.9182)\n centers[17] = (0.0231, 0.6946)\n centers[18] = (0.0231, 0.3054)\n centers[19] = (0.0186, 0.0818)\n centers[20] = (0.0222, 0.0136)\n centers[21] = (0.2452, 0.0206)\n centers[22] = (0.6420, 0.0237)\n centers[23] = (0.8962, 0.0179)\n centers[24] = (0.9845, 0.0201)\n centers[25] = (0.9816, 0.1821)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.2838169291759578}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/packing_viz.png
+ execution_time_mean: 0.022632477805018425
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..1d1942fbb1c3eaaac4cab5f141b770d7dfbe3a0e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.2838169291759578,
+ "public": {
+ "centers_str": " centers[0] = (0.4950, 0.5000)\n centers[1] = (0.9000, 0.5000)\n centers[2] = (0.8381, 0.8530)\n centers[3] = (0.4573, 0.9745)\n centers[4] = (0.1417, 0.8565)\n centers[5] = (0.0259, 0.5000)\n centers[6] = (0.1417, 0.1435)\n centers[7] = (0.4573, 0.0255)\n centers[8] = (0.8381, 0.1470)\n centers[9] = (0.9842, 0.5000)\n centers[10] = (0.9816, 0.8179)\n centers[11] = (0.9845, 0.9799)\n centers[12] = (0.8962, 0.9821)\n centers[13] = (0.6420, 0.9763)\n centers[14] = (0.2452, 0.9794)\n centers[15] = (0.0222, 0.9864)\n centers[16] = (0.0186, 0.9182)\n centers[17] = (0.0231, 0.6946)\n centers[18] = (0.0231, 0.3054)\n centers[19] = (0.0186, 0.0818)\n centers[20] = (0.0222, 0.0136)\n centers[21] = (0.2452, 0.0206)\n centers[22] = (0.6420, 0.0237)\n centers[23] = (0.8962, 0.0179)\n centers[24] = (0.9845, 0.0201)\n centers[25] = (0.9816, 0.1821)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.2838169291759578
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/results/packing_viz.png",
+ "execution_time_mean": 0.022632477805018425,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..541dd768654ebe2dc2525cab2765c3ce271ac5be
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_6/rewrite.txt
@@ -0,0 +1,119 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid circle packing for n=26 circles.
+Starts with a concentric ring pattern and refines positions using
+force-directed relaxation, then computes radii iteratively.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method, starting from a concentric ring pattern,
+ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with a structured concentric ring pattern.
+ # This provides a symmetric, ordered starting point for the relaxation,
+ # combining the idea from the first parent program.
+ centers = np.zeros((n, 2))
+
+ # Place a circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles in an inner ring
+ inner_ring_radius = 0.28
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
+
+ # Place the remaining 17 circles in an outer ring
+ outer_ring_radius = 0.48
+ for i in range(17):
+ angle = 2 * np.pi * i / 17
+ centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
+
+ # 2. Iterative Position Refinement (Force Simulation from the second parent)
+ n_iter = 250
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..62ffadb3c5f13604a4d969dd38b06e18607a8155
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..eadc59cd5d04a6e634bd63c08b052a0b6e6ae45b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/edit.diff
@@ -0,0 +1,260 @@
+--- a/original.py
++++ b/original.py
+@@ -1,216 +1,232 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ This version is a crossover, combining the high-level strategy of the best-performing
+ parent (multi-start, two-phase simulation, annealed growth pressure) with the
+ efficient vectorized force calculations from the inspiration program.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations for more thorough exploration
+ iterations = 1200
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.35 # Slightly softer walls than 0.5, but firmer than 0.25 in G37 for better boundary utilization
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+- # Candidate initial positions for the 26th circle to optimize corner utilization.
+- candidate_extra_circle_initial_positions = [
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+- [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
++ # Define a list of initial configuration strategies to diversify the multi-start.
++ initial_config_strategies = []
++
++ # Strategy 1: Perturbed 5x5 grid for 25 circles + a specific placement for the 26th circle.
++ # This covers organized, grid-like starting points, exploring various interstitial positions.
++ grid_plus_one_positions = [
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
++ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Inner corners
++ [0.5, 0.5], # Center of the square
++ [0.05, 0.5], [0.5, 0.05], [0.95, 0.5], [0.5, 0.95] # Mid-edge points
+ ]
+-
+- for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+- # --- Initialization for current trial ---
++ for pos_26th in grid_plus_one_positions:
++ initial_config_strategies.append({"type": "grid_plus_one", "pos_26th": pos_26th})
++
++ # Strategy 2: Completely random positions for all 26 circles.
++ # This is crucial for exploring non-grid-like arrangements and breaking initial symmetries.
++ num_random_starts = 5 # Add 5 fully random starting configurations
++ for _ in range(num_random_starts):
++ initial_config_strategies.append({"type": "random"})
++
++ # Loop through each defined initial configuration strategy
++ for strategy_info in initial_config_strategies:
+ current_centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- current_centers[k, 0] = (i + 0.5) * spacing
+- current_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+- perturbation_scale = 0.002 # A small fraction of the spacing
+- current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+-
+- current_centers[25] = initial_pos_26th_circle
++
++ if strategy_info["type"] == "grid_plus_one":
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for i in range(num_cells_side):
++ for j in range(num_cells_side):
++ current_centers[k, 0] = (i + 0.5) * spacing
++ current_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++ # Add small random perturbation to the 5x5 grid (first 25 circles)
++ perturbation_scale = 0.002 # A small fraction of the spacing
++ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
++ current_centers[25] = strategy_info["pos_26th"]
++ elif strategy_info["type"] == "random":
++ current_centers = np.random.rand(n, 2)
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+-
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+- # Anneal growth pressure
+- current_growth_pressure = initial_growth_pressure - \
+- (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
++ # Anneal growth pressure quadratically for better exploration in early stages
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * \
++ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 200 # Increased iterations for more precise settling
+ fine_tune_learning_rate = 0.0005 # Even smaller LR for very fine adjustments
+ fine_tune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ fine_tune_growth_pressure_end = 1.00001 # Anneal it down to almost none for final precision
+ centers_for_sim = best_final_centers.copy()
+
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (similar to main phase, but milder)
+ current_fine_tune_growth_pressure = fine_tune_growth_pressure_start - \
+ (fine_tune_growth_pressure_start - fine_tune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)**2 # Quadratic annealing for growth pressure
+ inflated_radii = radii * current_fine_tune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_fine_tune = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing for fine-tune LR
+ centers_for_sim += forces * current_lr_fine_tune
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..23d9bd4c51ecd4b4dcdfb69c354ddf4a7e9995fe
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/main.py
@@ -0,0 +1,232 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations for more thorough exploration
+ iterations = 1200
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.35 # Slightly softer walls than 0.5, but firmer than 0.25 in G37 for better boundary utilization
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Define a list of initial configuration strategies to diversify the multi-start.
+ initial_config_strategies = []
+
+ # Strategy 1: Perturbed 5x5 grid for 25 circles + a specific placement for the 26th circle.
+ # This covers organized, grid-like starting points, exploring various interstitial positions.
+ grid_plus_one_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Inner corners
+ [0.5, 0.5], # Center of the square
+ [0.05, 0.5], [0.5, 0.05], [0.95, 0.5], [0.5, 0.95] # Mid-edge points
+ ]
+ for pos_26th in grid_plus_one_positions:
+ initial_config_strategies.append({"type": "grid_plus_one", "pos_26th": pos_26th})
+
+ # Strategy 2: Completely random positions for all 26 circles.
+ # This is crucial for exploring non-grid-like arrangements and breaking initial symmetries.
+ num_random_starts = 5 # Add 5 fully random starting configurations
+ for _ in range(num_random_starts):
+ initial_config_strategies.append({"type": "random"})
+
+ # Loop through each defined initial configuration strategy
+ for strategy_info in initial_config_strategies:
+ current_centers = np.zeros((n, 2))
+
+ if strategy_info["type"] == "grid_plus_one":
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Add small random perturbation to the 5x5 grid (first 25 circles)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+ current_centers[25] = strategy_info["pos_26th"]
+ elif strategy_info["type"] == "random":
+ current_centers = np.random.rand(n, 2)
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically for better exploration in early stages
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 200 # Increased iterations for more precise settling
+ fine_tune_learning_rate = 0.0005 # Even smaller LR for very fine adjustments
+ fine_tune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ fine_tune_growth_pressure_end = 1.00001 # Anneal it down to almost none for final precision
+ centers_for_sim = best_final_centers.copy()
+
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (similar to main phase, but milder)
+ current_fine_tune_growth_pressure = fine_tune_growth_pressure_start - \
+ (fine_tune_growth_pressure_start - fine_tune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)**2 # Quadratic annealing for growth pressure
+ inflated_radii = radii * current_fine_tune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_fine_tune = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing for fine-tune LR
+ centers_for_sim += forces * current_lr_fine_tune
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0a61ab6c7ce7a95da3976cc0ac2e9d4062b84b8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/original.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Increased iterations for more thorough exploration
+ iterations = 1200
+ learning_rate = 0.01
+ wall_repulsion_strength = 0.35 # Slightly softer walls than 0.5, but firmer than 0.25 in G37 for better boundary utilization
+ initial_growth_pressure = 1.018 # Slightly higher initial pressure for stronger repulsion
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 200 # Increased iterations for more precise settling
+ fine_tune_learning_rate = 0.0005 # Even smaller LR for very fine adjustments
+ fine_tune_growth_pressure_start = 1.0005 # Subtle growth pressure (Recommendation 2)
+ fine_tune_growth_pressure_end = 1.00001 # Anneal it down to almost none for final precision
+ centers_for_sim = best_final_centers.copy()
+
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (similar to main phase, but milder)
+ current_fine_tune_growth_pressure = fine_tune_growth_pressure_start - \
+ (fine_tune_growth_pressure_start - fine_tune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)**2 # Quadratic annealing for growth pressure
+ inflated_radii = radii * current_fine_tune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_fine_tune = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing for fine-tune LR
+ centers_for_sim += forces * current_lr_fine_tune
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..5a6269f299a85afa6a1146b1d1c7293aff1dac8c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results
+Run 1/1 completed in 58.75 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.935163047592651
+ public: {'centers_str': ' centers[0] = (0.1003, 0.0996)\n centers[1] = (0.1003, 0.2999)\n centers[2] = (0.1002, 0.4999)\n centers[3] = (0.1001, 0.7002)\n centers[4] = (0.1007, 0.8996)\n centers[5] = (0.3042, 0.0954)\n centers[6] = (0.2997, 0.2997)\n centers[7] = (0.3001, 0.4987)\n centers[8] = (0.2996, 0.6995)\n centers[9] = (0.2998, 0.8998)\n centers[10] = (0.5001, 0.1001)\n centers[11] = (0.5003, 0.3002)\n centers[12] = (0.5002, 0.5000)\n centers[13] = (0.4999, 0.7001)\n centers[14] = (0.4996, 0.8998)\n centers[15] = (0.6996, 0.1002)\n centers[16] = (0.7000, 0.2999)\n centers[17] = (0.6999, 0.4952)\n centers[18] = (0.6947, 0.6980)\n centers[19] = (0.6999, 0.9053)\n centers[20] = (0.8994, 0.0979)\n centers[21] = (0.8982, 0.2985)\n centers[22] = (0.8984, 0.5029)\n centers[23] = (0.9039, 0.7019)\n centers[24] = (0.8993, 0.8997)\n centers[25] = (0.1955, 0.2045)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.935163047592651}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/packing_viz.png
+ execution_time_mean: 58.752373822033405
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..6bbbbfdd7d8056d3818f3000bd4c1acc0e941a6c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.935163047592651,
+ "public": {
+ "centers_str": " centers[0] = (0.1003, 0.0996)\n centers[1] = (0.1003, 0.2999)\n centers[2] = (0.1002, 0.4999)\n centers[3] = (0.1001, 0.7002)\n centers[4] = (0.1007, 0.8996)\n centers[5] = (0.3042, 0.0954)\n centers[6] = (0.2997, 0.2997)\n centers[7] = (0.3001, 0.4987)\n centers[8] = (0.2996, 0.6995)\n centers[9] = (0.2998, 0.8998)\n centers[10] = (0.5001, 0.1001)\n centers[11] = (0.5003, 0.3002)\n centers[12] = (0.5002, 0.5000)\n centers[13] = (0.4999, 0.7001)\n centers[14] = (0.4996, 0.8998)\n centers[15] = (0.6996, 0.1002)\n centers[16] = (0.7000, 0.2999)\n centers[17] = (0.6999, 0.4952)\n centers[18] = (0.6947, 0.6980)\n centers[19] = (0.6999, 0.9053)\n centers[20] = (0.8994, 0.0979)\n centers[21] = (0.8982, 0.2985)\n centers[22] = (0.8984, 0.5029)\n centers[23] = (0.9039, 0.7019)\n centers[24] = (0.8993, 0.8997)\n centers[25] = (0.1955, 0.2045)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.935163047592651
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/results/packing_viz.png",
+ "execution_time_mean": 58.752373822033405,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..03cd0b226227fba5db8fd5e789e3ad0042d43515
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_60/search_replace.txt
@@ -0,0 +1,109 @@
+
+diverse_initial_configs_and_quadratic_annealing
+
+
+
+This edit introduces two main improvements to enhance the exploration capabilities and annealing schedule of the physical simulation:
+
+1. **More Diverse Initial Configurations**: The original `construct_packing` primarily relied on a perturbed 5x5 grid for 25 circles, with only the 26th circle's initial position varying. This approach, while providing some structured starting points, can limit the exploration of optimal non-grid-like packings. This change broadens the multi-start strategy by:
+ * Expanding the set of specific initial positions for the 26th circle (including center and mid-edge points) when starting from a 5x5 grid.
+ * Adding several "fully random" initial configurations for all 26 circles. This is crucial for breaking potential symmetries and allowing the simulation to discover completely different, potentially more efficient, topological arrangements. This increases the chance of finding higher-sum-of-radii packings by exploring a wider range of the solution space.
+
+2. **Quadratic Annealing for Main Growth Pressure**: The main simulation loop's `current_growth_pressure` annealing was linear in the current version. Reverting this to a quadratic decay (as seen in prior successful generations that showed better performance) means the growth pressure decreases slower initially and then accelerates its reduction. This allows for stronger and more persistent exploration (larger center movements) in the early stages of the simulation, facilitating escape from local optima. As the simulation progresses, the growth pressure reduces more rapidly, leading to a smoother transition towards fine-tuning the packing in later iterations.
+
+
+
+<<<<<<< SEARCH
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, Recommendation 4)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure
+ current_growth_pressure = initial_growth_pressure - \
+ (initial_growth_pressure - final_growth_pressure) * (sim_iter / iterations)
+ inflated_radii = radii * current_growth_pressure
+=======
+ # Define a list of initial configuration strategies to diversify the multi-start.
+ initial_config_strategies = []
+
+ # Strategy 1: Perturbed 5x5 grid for 25 circles + a specific placement for the 26th circle.
+ # This covers organized, grid-like starting points, exploring various interstitial positions.
+ grid_plus_one_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Inner corners
+ [0.5, 0.5], # Center of the square
+ [0.05, 0.5], [0.5, 0.05], [0.95, 0.5], [0.5, 0.95] # Mid-edge points
+ ]
+ for pos_26th in grid_plus_one_positions:
+ initial_config_strategies.append({"type": "grid_plus_one", "pos_26th": pos_26th})
+
+ # Strategy 2: Completely random positions for all 26 circles.
+ # This is crucial for exploring non-grid-like arrangements and breaking initial symmetries.
+ num_random_starts = 5 # Add 5 fully random starting configurations
+ for _ in range(num_random_starts):
+ initial_config_strategies.append({"type": "random"})
+
+ # Loop through each defined initial configuration strategy
+ for strategy_info in initial_config_strategies:
+ current_centers = np.zeros((n, 2))
+
+ if strategy_info["type"] == "grid_plus_one":
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Add small random perturbation to the 5x5 grid (first 25 circles)
+ perturbation_scale = 0.002 # A small fraction of the spacing
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+ current_centers[25] = strategy_info["pos_26th"]
+ elif strategy_info["type"] == "random":
+ current_centers = np.random.rand(n, 2)
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically for better exploration in early stages
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a219ffba428a45d6dbdb6891d66691af98a09ec9
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..9b8dfc196e22aad2b274b3e2e099ae10bac9e574
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/edit.diff
@@ -0,0 +1,276 @@
+--- a/original.py
++++ b/original.py
+@@ -1,216 +1,233 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ This version refines the annealing strategy and diversifies initial start
+ positions to improve the global search and final packing quality.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned parameters for a strong but controlled "explosion" and robust fine-tuning.
+- iterations = 2200 # Increased iterations for more thorough evolution.
+- learning_rate = 0.018 # Slightly reduced LR for stability.
+- wall_repulsion_strength = 0.4 # Increased wall strength to better contain the initial expansion.
+- initial_growth_pressure = 1.04 # Moderated initial pressure for a less chaotic "explosion".
++ iterations = 2500 # Further increased iterations for thorough exploration with higher forces.
++ learning_rate = 0.025 # Increased LR for more significant center movements during the main phase.
++ wall_repulsion_strength = 0.5 # Increased wall strength to better contain the more aggressive initial expansion.
++ initial_growth_pressure = 1.06 # Significantly higher initial pressure for a stronger "explosion" phase.
+ final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+- # Expanded candidate positions for the 26th circle to diversify initial states (Recommendation 1).
+- candidate_extra_circle_initial_positions = [
+- # Corners (deep)
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+- # Interstitial voids (corner-like)
+- [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+- # Interstitial voids (central)
+- [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
++ # Structured initial positions for the 26th circle, assuming a 5x5 grid for the first 25.
++ # These configurations aim to leverage known patterns and boundary effects.
++ structured_extra_circle_positions = [
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
++ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial voids (corner-like)
++ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6], # Interstitial voids (central)
+ ]
+-
+- for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+- # --- Initialization for current trial ---
++ num_random_initial_trials = 4 # Number of trials starting with completely random circle positions
++
++ # Combine structured and random initializations
++ for trial_idx in range(len(structured_extra_circle_positions) + num_random_initial_trials):
+ current_centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- current_centers[k, 0] = (i + 0.5) * spacing
+- current_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+- perturbation_scale = 0.008
+- current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+-
+- current_centers[25] = initial_pos_26th_circle
+-
++
++ if trial_idx < len(structured_extra_circle_positions):
++ # Structured start: 5x5 grid for 25 circles, plus a specific 26th circle
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for i in range(num_cells_side):
++ for j in range(num_cells_side):
++ current_centers[k, 0] = (i + 0.5) * spacing
++ current_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
++ perturbation_scale = 0.008
++ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
++ current_centers[25] = structured_extra_circle_positions[trial_idx]
++ else:
++ # Fully random start for all circles to explore entirely different configurations
++ current_centers = np.random.rand(n, 2)
++
++ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Reverted to QUADRATIC annealing for growth pressure for a fast-subsiding push.
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 500 # Increased fine-tune iterations for more precise settling.
+ fine_tune_learning_rate = 0.001 # Kept fine-tuning LR.
+ finetune_growth_pressure_start = 1.0008 # A slightly stronger nudge to escape shallow local minima.
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end.
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability).
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay).
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+- # Increased iterations for better convergence and accuracy per simulation step.
+- for _ in range(600):
+- updated_in_pass = False
++ max_radius_iter = 600 # Maximum iterations for radius calculation
++ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
++ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
++ converged_in_a_row = 0 # Counter for consecutive converged iterations
++
++ for iteration in range(max_radius_iter):
++ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
++
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+- if dist < 1e-9:
++ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+- updated_in_pass = True
++ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0.0)
++ # Calculate the maximum absolute change in any radius
++ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
++
++ # Check for convergence based on max_absolute_change
++ if max_absolute_change < convergence_epsilon:
++ converged_in_a_row += 1
++ if converged_in_a_row >= required_converged_iters:
++ break # Converged for several consecutive iterations
++ else:
++ converged_in_a_row = 0 # Reset counter if change is significant
++
++ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..25afe75b90a8b15895759a8f164593991fd003fa
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/main.py
@@ -0,0 +1,233 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version refines the annealing strategy and diversifies initial start
+positions to improve the global search and final packing quality.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned parameters for a strong but controlled "explosion" and robust fine-tuning.
+ iterations = 2500 # Further increased iterations for thorough exploration with higher forces.
+ learning_rate = 0.025 # Increased LR for more significant center movements during the main phase.
+ wall_repulsion_strength = 0.5 # Increased wall strength to better contain the more aggressive initial expansion.
+ initial_growth_pressure = 1.06 # Significantly higher initial pressure for a stronger "explosion" phase.
+ final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Structured initial positions for the 26th circle, assuming a 5x5 grid for the first 25.
+ # These configurations aim to leverage known patterns and boundary effects.
+ structured_extra_circle_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial voids (corner-like)
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6], # Interstitial voids (central)
+ ]
+ num_random_initial_trials = 4 # Number of trials starting with completely random circle positions
+
+ # Combine structured and random initializations
+ for trial_idx in range(len(structured_extra_circle_positions) + num_random_initial_trials):
+ current_centers = np.zeros((n, 2))
+
+ if trial_idx < len(structured_extra_circle_positions):
+ # Structured start: 5x5 grid for 25 circles, plus a specific 26th circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+ perturbation_scale = 0.008
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+ current_centers[25] = structured_extra_circle_positions[trial_idx]
+ else:
+ # Fully random start for all circles to explore entirely different configurations
+ current_centers = np.random.rand(n, 2)
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Reverted to QUADRATIC annealing for growth pressure for a fast-subsiding push.
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 500 # Increased fine-tune iterations for more precise settling.
+ fine_tune_learning_rate = 0.001 # Kept fine-tuning LR.
+ finetune_growth_pressure_start = 1.0008 # A slightly stronger nudge to escape shallow local minima.
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end.
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability).
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay).
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..b60f23b7bb16fd88f56276775e7843436a7dab5b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/original.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version refines the annealing strategy and diversifies initial start
+positions to improve the global search and final packing quality.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned parameters for a strong but controlled "explosion" and robust fine-tuning.
+ iterations = 2200 # Increased iterations for more thorough evolution.
+ learning_rate = 0.018 # Slightly reduced LR for stability.
+ wall_repulsion_strength = 0.4 # Increased wall strength to better contain the initial expansion.
+ initial_growth_pressure = 1.04 # Moderated initial pressure for a less chaotic "explosion".
+ final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Expanded candidate positions for the 26th circle to diversify initial states (Recommendation 1).
+ candidate_extra_circle_initial_positions = [
+ # Corners (deep)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ # Interstitial voids (corner-like)
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ # Interstitial voids (central)
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+ perturbation_scale = 0.008
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Reverted to QUADRATIC annealing for growth pressure for a fast-subsiding push.
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 500 # Increased fine-tune iterations for more precise settling.
+ fine_tune_learning_rate = 0.001 # Kept fine-tuning LR.
+ finetune_growth_pressure_start = 1.0008 # A slightly stronger nudge to escape shallow local minima.
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end.
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability).
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay).
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Increased iterations for better convergence and accuracy per simulation step.
+ for _ in range(600):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..d05974a6f02625746afa05b0e090974c3cc7f283
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results
+Run 1/1 completed in 268.59 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9360203478373788
+ public: {'centers_str': ' centers[0] = (0.1023, 0.0973)\n centers[1] = (0.0976, 0.2992)\n centers[2] = (0.0997, 0.5011)\n centers[3] = (0.1001, 0.6997)\n centers[4] = (0.0954, 0.8978)\n centers[5] = (0.2993, 0.1010)\n centers[6] = (0.2252, 0.2264)\n centers[7] = (0.2554, 0.5483)\n centers[8] = (0.2996, 0.6996)\n centers[9] = (0.2984, 0.8893)\n centers[10] = (0.5009, 0.0997)\n centers[11] = (0.5493, 0.2598)\n centers[12] = (0.5533, 0.5382)\n centers[13] = (0.4347, 0.6908)\n centers[14] = (0.4827, 0.9203)\n centers[15] = (0.7035, 0.0772)\n centers[16] = (0.6750, 0.2370)\n centers[17] = (0.6865, 0.4311)\n centers[18] = (0.7007, 0.7433)\n centers[19] = (0.7051, 0.9719)\n centers[20] = (0.9023, 0.0856)\n centers[21] = (0.8760, 0.2944)\n centers[22] = (0.9004, 0.5178)\n centers[23] = (0.9489, 0.6995)\n centers[24] = (0.9197, 0.9193)\n centers[25] = (0.3849, 0.3829)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9360203478373788}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/packing_viz.png
+ execution_time_mean: 268.5896864430979
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..c8ee0ccde381ec4911394b8e64e66e34410b2b25
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9360203478373788,
+ "public": {
+ "centers_str": " centers[0] = (0.1023, 0.0973)\n centers[1] = (0.0976, 0.2992)\n centers[2] = (0.0997, 0.5011)\n centers[3] = (0.1001, 0.6997)\n centers[4] = (0.0954, 0.8978)\n centers[5] = (0.2993, 0.1010)\n centers[6] = (0.2252, 0.2264)\n centers[7] = (0.2554, 0.5483)\n centers[8] = (0.2996, 0.6996)\n centers[9] = (0.2984, 0.8893)\n centers[10] = (0.5009, 0.0997)\n centers[11] = (0.5493, 0.2598)\n centers[12] = (0.5533, 0.5382)\n centers[13] = (0.4347, 0.6908)\n centers[14] = (0.4827, 0.9203)\n centers[15] = (0.7035, 0.0772)\n centers[16] = (0.6750, 0.2370)\n centers[17] = (0.6865, 0.4311)\n centers[18] = (0.7007, 0.7433)\n centers[19] = (0.7051, 0.9719)\n centers[20] = (0.9023, 0.0856)\n centers[21] = (0.8760, 0.2944)\n centers[22] = (0.9004, 0.5178)\n centers[23] = (0.9489, 0.6995)\n centers[24] = (0.9197, 0.9193)\n centers[25] = (0.3849, 0.3829)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9360203478373788
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/results/packing_viz.png",
+ "execution_time_mean": 268.5896864430979,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..261125988ebcae872aaf0596985be20ad08caf07
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_61/search_replace.txt
@@ -0,0 +1,176 @@
+I will propose three edits. The first will introduce fully random initial configurations alongside the structured grid starts, increasing the diversity of the starting points for the simulation. The second edit will refine the convergence criteria for `compute_max_radii`, making it more robust and potentially more efficient by terminating when radii changes are consistently negligible. The third edit will adjust the main phase hyperparameters to encourage a more aggressive initial "explosion" and broader exploration of the solution space, which is critical for escaping local optima in physical simulation methods.
+
+
+random_initial_configs
+
+
+
+To enhance the global search capability, this edit diversifies the initial configurations for the simulation. Instead of always starting from a perturbed 5x5 grid with the 26th circle placed at a specific interstitial point, a subset of trials will now begin with all 26 circles placed entirely randomly within the unit square. This allows the simulation to explore fundamentally different packing patterns that might not be discoverable from grid-based starts, especially when aiming for higher packing densities. This addresses the "Diversify the 'Known Good' Initial Configurations" recommendation by introducing true random starts.
+
+
+
+<<<<<<< SEARCH
+ # Expanded candidate positions for the 26th circle to diversify initial states (Recommendation 1).
+ candidate_extra_circle_initial_positions = [
+ # Corners (deep)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ # Interstitial voids (corner-like)
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ # Interstitial voids (central)
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+ perturbation_scale = 0.008
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+=======
+ # Structured initial positions for the 26th circle, assuming a 5x5 grid for the first 25.
+ # These configurations aim to leverage known patterns and boundary effects.
+ structured_extra_circle_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial voids (corner-like)
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6], # Interstitial voids (central)
+ ]
+ num_random_initial_trials = 4 # Number of trials starting with completely random circle positions
+
+ # Combine structured and random initializations
+ for trial_idx in range(len(structured_extra_circle_positions) + num_random_initial_trials):
+ current_centers = np.zeros((n, 2))
+
+ if trial_idx < len(structured_extra_circle_positions):
+ # Structured start: 5x5 grid for 25 circles, plus a specific 26th circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+ perturbation_scale = 0.008
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+ current_centers[25] = structured_extra_circle_positions[trial_idx]
+ else:
+ # Fully random start for all circles to explore entirely different configurations
+ current_centers = np.random.rand(n, 2)
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+>>>>>>> REPLACE
+
+
+
+dynamic_radius_convergence
+
+
+
+This edit improves the `compute_max_radii` function by implementing a more robust and precise convergence criterion. Instead of relying solely on a boolean flag (`updated_in_pass`) or a fixed number of iterations, the function now tracks the maximum absolute change in any radius during each pass. The loop terminates if this maximum change falls below a small epsilon (`1e-7`) for a specified number of consecutive iterations (`required_converged_iters`). This ensures that the radii are calculated with high accuracy while avoiding unnecessary computations once a stable state is reached, addressing "Refine `compute_max_radii` with a More Robust Dynamic Convergence Criterion" recommendation.
+
+
+
+<<<<<<< SEARCH
+ # Increased iterations for better convergence and accuracy per simulation step.
+ for _ in range(600):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+=======
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+>>>>>>> REPLACE
+
+
+
+aggressive_main_phase_tuning
+
+
+
+This edit fine-tunes the hyperparameters for the main simulation phase to promote a more aggressive exploration strategy. The `iterations` are increased to allow more time for the simulation to settle with higher forces. The `learning_rate` and `initial_growth_pressure` are both increased to create a stronger "explosion" effect, forcing circles to rapidly move apart and explore new packing configurations, especially useful when combined with the new random initial states. The `wall_repulsion_strength` is also slightly increased to better contain the more energetic circles within the unit square. These adjustments aim to help the simulation overcome local minima and achieve higher overall sum of radii.
+
+
+
+<<<<<<< SEARCH
+ iterations = 2200 # Increased iterations for more thorough evolution.
+ learning_rate = 0.018 # Slightly reduced LR for stability.
+ wall_repulsion_strength = 0.4 # Increased wall strength to better contain the initial expansion.
+ initial_growth_pressure = 1.04 # Moderated initial pressure for a less chaotic "explosion".
+ final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+=======
+ iterations = 2500 # Further increased iterations for thorough exploration with higher forces.
+ learning_rate = 0.025 # Increased LR for more significant center movements during the main phase.
+ wall_repulsion_strength = 0.5 # Increased wall strength to better contain the more aggressive initial expansion.
+ initial_growth_pressure = 1.06 # Significantly higher initial pressure for a stronger "explosion" phase.
+ final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6177d04871c59a30f0ab2792be81ed5fb5ea8e22
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..7a1027aa174df94c10ebb8ab4705a0c3ba2b35fe
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/edit.diff
@@ -0,0 +1,368 @@
+--- a/original.py
++++ b/original.py
+@@ -1,309 +1,300 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+- # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
++ best_sum_radii = -1.0 # Initialize with a value lower than any possible sum of radii
++ best_centers = None
++ best_radii = None
++
++ # --- Simulation Hyperparameters ---
++ # Tuned based on high-scoring prior programs.
++ simulation_iterations = 1000 # Increased iterations for thorough refinement.
++ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
++ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
++ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
++ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
++ fine_tune_iter = 200 # Increased iterations for final settlement.
++ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
++
++ # --- Generate Diverse Initial Configurations ---
++ all_initial_configs = []
++
++ # Strategy 1: Fixed 5x5 grid for 25 circles + strategic placements for the 26th
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+- base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
++ base_centers_5x5 = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+- base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+- base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
++ base_centers_5x5[k, 0] = (i + 0.5) * spacing
++ base_centers_5x5[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+- # Define strategic candidate placements for the 26th circle.
+- # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+- # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- # Corner-offset points
+- [0.05, 0.05],
++ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+- best_sum_radii = 0
+- best_centers = None
+- best_radii = None
+-
+- # --- Simulation Hyperparameters ---
+- # Tuned based on high-scoring prior programs.
+- simulation_iterations = 1000 # Increased iterations for thorough refinement.
+- initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+- wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+- initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+- final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+- fine_tune_iter = 200 # Increased iterations for final settlement.
+- fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+-
+- # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+- current_centers = np.zeros((n, 2))
+- current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+- current_centers[25] = np.array(extra_pos)
+-
++ config = np.zeros((n, 2))
++ config[:n-1] = base_centers_5x5.copy()
++ config[n-1] = np.array(extra_pos)
++ all_initial_configs.append(config)
++
++ # Strategy 2: Randomly perturbed versions of a "known good" 5x5 + interstitial base
++ # Using [spacing, spacing] as the interstitial for this perturbation
++ strong_5x5_base = np.zeros((n, 2))
++ strong_5x5_base[:n-1] = base_centers_5x5.copy()
++ strong_5x5_base[n-1] = np.array([spacing, spacing]) # A common good starting point for 26th
++
++ for _ in range(8): # Add multiple perturbed versions
++ perturbed_config = strong_5x5_base + np.random.normal(0, 0.02, (n, 2)) # Small Gaussian noise
++ perturbed_config = np.clip(perturbed_config, 0.0, 1.0) # Ensure within bounds
++ all_initial_configs.append(perturbed_config)
++
++ # Strategy 3: Completely random configurations for broad exploration
++ for _ in range(5): # Add a few completely random configurations
++ random_config = np.random.rand(n, 2)
++ all_initial_configs.append(random_config)
++
++ # --- Iterate through all diverse initial configurations and run the simulation ---
++ for initial_config_to_try in all_initial_configs:
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+- centers_for_sim = current_centers.copy() # Operate on a copy for simulation
++ centers_for_sim = initial_config_to_try.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b. Wall repulsion forces (based on pressured radii)
+- for i in range(n):
+- # Left wall
+- overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
++ # b. Wall repulsion forces (vectorized, based on pressured radii)
++ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left wall
++ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right wall
++ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom wall
++ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b. Wall repulsion forces (based on actual radii)
+- for i in range(n):
+- # Left wall
+- overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
++ # b. Wall repulsion forces (vectorized, based on actual radii)
++ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0]) # Left wall
++ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0) # Right wall
++ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1]) # Bottom wall
++ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0) # Top wall
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+ def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..d4fc752d417dae5d21615dab5be74764845ba78f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/main.py
@@ -0,0 +1,300 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ best_sum_radii = -1.0 # Initialize with a value lower than any possible sum of radii
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 1000 # Increased iterations for thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 200 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # --- Generate Diverse Initial Configurations ---
+ all_initial_configs = []
+
+ # Strategy 1: Fixed 5x5 grid for 25 circles + strategic placements for the 26th
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers_5x5 = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers_5x5[k, 0] = (i + 0.5) * spacing
+ base_centers_5x5[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ config[:n-1] = base_centers_5x5.copy()
+ config[n-1] = np.array(extra_pos)
+ all_initial_configs.append(config)
+
+ # Strategy 2: Randomly perturbed versions of a "known good" 5x5 + interstitial base
+ # Using [spacing, spacing] as the interstitial for this perturbation
+ strong_5x5_base = np.zeros((n, 2))
+ strong_5x5_base[:n-1] = base_centers_5x5.copy()
+ strong_5x5_base[n-1] = np.array([spacing, spacing]) # A common good starting point for 26th
+
+ for _ in range(8): # Add multiple perturbed versions
+ perturbed_config = strong_5x5_base + np.random.normal(0, 0.02, (n, 2)) # Small Gaussian noise
+ perturbed_config = np.clip(perturbed_config, 0.0, 1.0) # Ensure within bounds
+ all_initial_configs.append(perturbed_config)
+
+ # Strategy 3: Completely random configurations for broad exploration
+ for _ in range(5): # Add a few completely random configurations
+ random_config = np.random.rand(n, 2)
+ all_initial_configs.append(random_config)
+
+ # --- Iterate through all diverse initial configurations and run the simulation ---
+ for initial_config_to_try in all_initial_configs:
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = initial_config_to_try.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left wall
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right wall
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom wall
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0]) # Left wall
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0) # Right wall
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1]) # Bottom wall
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0) # Top wall
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fc8c7dbfd5b6e3983eb39b62837085607d66e9d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/original.py
@@ -0,0 +1,309 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 1000 # Increased iterations for thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 200 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (based on actual radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..96ee62bfd3cb6ab5164606cd45452ffee27ac3cf
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results
+Run 1/1 completed in 375.86 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9454836647028526
+ public: {'centers_str': ' centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3029, 0.0971)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8990, 0.1010)\n centers[5] = (0.0918, 0.3082)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.4861, 0.6984)\n centers[18] = (0.6994, 0.6893)\n centers[19] = (0.9094, 0.7000)\n centers[20] = (0.0945, 0.8996)\n centers[21] = (0.2951, 0.8939)\n centers[22] = (0.5054, 0.8959)\n centers[23] = (0.7049, 0.9046)\n centers[24] = (0.9003, 0.8985)\n centers[25] = (0.2053, 0.1947)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9454836647028526}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/packing_viz.png
+ execution_time_mean: 375.8563929479569
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..a0839f17ae45538ef84c8e34fd3c7a185ee749fc
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9454836647028526,
+ "public": {
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.3029, 0.0971)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8990, 0.1010)\n centers[5] = (0.0918, 0.3082)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.7000, 0.5000)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.3000, 0.7000)\n centers[17] = (0.4861, 0.6984)\n centers[18] = (0.6994, 0.6893)\n centers[19] = (0.9094, 0.7000)\n centers[20] = (0.0945, 0.8996)\n centers[21] = (0.2951, 0.8939)\n centers[22] = (0.5054, 0.8959)\n centers[23] = (0.7049, 0.9046)\n centers[24] = (0.9003, 0.8985)\n centers[25] = (0.2053, 0.1947)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9454836647028526
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/results/packing_viz.png",
+ "execution_time_mean": 375.8563929479569,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6ad74effbb9bb1b3ad3a7ffe6e70a85b79fe684c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_62/search_replace.txt
@@ -0,0 +1,211 @@
+
+vectorize_wall_forces
+
+
+
+The previous program versions included vectorized wall force calculations, which are more efficient than iterating through each circle. This edit re-introduces vectorized wall repulsion to improve the performance (speed) of the simulation loops. This will allow for potentially more iterations or more diverse initial configurations within the same time budget, contributing to better overall solution quality. The current code has explicit `for i in range(n)` loops for wall forces, which is less efficient.
+
+
+
+<<<<<<< SEARCH
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+=======
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left wall
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right wall
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom wall
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top wall
+>>>>>>> REPLACE
+
+
+
+<<<<<<< SEARCH
+ # b. Wall repulsion forces (based on actual radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+=======
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0]) # Left wall
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0) # Right wall
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1]) # Bottom wall
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0) # Top wall
+>>>>>>> REPLACE
+
+
+
+diversify_initial_configs
+
+
+
+The previous program versions showed that a Genetic Algorithm approach (which intrinsically explores a wide range of initial configurations) yielded higher scores than a simulation starting from a mostly fixed 5x5 grid. This suggests the current 'multi-start' strategy is too constrained. This edit diversifies the initial configurations passed to the force-directed simulation.
+
+Instead of only having 25 circles in a perfect 5x5 grid and varying the 26th, this change introduces:
+1. **Multiple fixed 5x5 + interstitial variants:** The existing `candidate_extra_positions` are used to generate full initial configurations.
+2. **Perturbed 5x5 + interstitial variants:** A 'known good' interstitial configuration (e.g., the 26th circle at (0.2, 0.2) relative to the 5x5 grid) is taken, and *all* 26 circles are slightly perturbed with Gaussian noise. This helps break the rigidity of the perfect grid and allows the simulation to explore nearby, possibly asymmetric, solutions.
+3. **Completely random configurations:** A few sets of 26 entirely random circle positions are added. This ensures broad exploration of the solution space, similar to a GA's initial population, allowing the simulation to escape local optima associated with grid-like starting points.
+
+The simulation will then run for each of these diverse initial configurations, and the best result will be selected. This significantly increases the exploration capability of the algorithm without changing the core simulation logic.
+
+
+
+<<<<<<< SEARCH
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 1000 # Increased iterations for thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 200 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+=======
+ best_sum_radii = -1.0 # Initialize with a value lower than any possible sum of radii
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 1000 # Increased iterations for thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 200 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # --- Generate Diverse Initial Configurations ---
+ all_initial_configs = []
+
+ # Strategy 1: Fixed 5x5 grid for 25 circles + strategic placements for the 26th
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers_5x5 = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers_5x5[k, 0] = (i + 0.5) * spacing
+ base_centers_5x5[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ config[:n-1] = base_centers_5x5.copy()
+ config[n-1] = np.array(extra_pos)
+ all_initial_configs.append(config)
+
+ # Strategy 2: Randomly perturbed versions of a "known good" 5x5 + interstitial base
+ # Using [spacing, spacing] as the interstitial for this perturbation
+ strong_5x5_base = np.zeros((n, 2))
+ strong_5x5_base[:n-1] = base_centers_5x5.copy()
+ strong_5x5_base[n-1] = np.array([spacing, spacing]) # A common good starting point for 26th
+
+ for _ in range(8): # Add multiple perturbed versions
+ perturbed_config = strong_5x5_base + np.random.normal(0, 0.02, (n, 2)) # Small Gaussian noise
+ perturbed_config = np.clip(perturbed_config, 0.0, 1.0) # Ensure within bounds
+ all_initial_configs.append(perturbed_config)
+
+ # Strategy 3: Completely random configurations for broad exploration
+ for _ in range(5): # Add a few completely random configurations
+ random_config = np.random.rand(n, 2)
+ all_initial_configs.append(random_config)
+
+ # --- Iterate through all diverse initial configurations and run the simulation ---
+ for initial_config_to_try in all_initial_configs:
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = initial_config_to_try.copy() # Operate on a copy for simulation
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..1a89155110743d6d424ef8db2853849e8d035fc4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/edit.diff
@@ -0,0 +1,258 @@
+--- a/original.py
++++ b/original.py
+@@ -1,236 +1,250 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+ This algorithm iteratively refines circle positions to maximize radii sum.
+ Improved by combining insights from high-scoring prior programs:
+ - Optimized initial placement of the 26th circle.
+ - Reduced initial perturbation noise.
+ - Tuned learning rate for fine-grained adjustments.
+ - Increased iterations for radius calculation convergence.
+ - Robust handling of very close circle centers in radius calculation.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers. This version incorporates:
+ - Multi-start initial placement for the 26th circle.
+ - A two-phase force-directed simulation (annealed growth pressure + fine-tuning).
+ - Vectorized wall repulsion for efficiency.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ sim_iter = 1500 # Increased iterations for thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 500 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_calc_sim_iter = 150 # Iterations for radii during simulation (lower for speed).
+ radius_calc_final_iter = 500 # Iterations for final radii calculation (higher for precision).
+
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset points.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
++ # Add a small random perturbation to the base grid centers
++ perturbation_strength = 0.005 * spacing # E.g., 5% of grid spacing
++ current_centers[:n-1] += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers[n-1] = np.array(extra_pos)
+
+- centers_for_sim = current_centers.copy() # Work on a copy for the simulation
++ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Work on a copy and ensure bounds
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically.
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers_for_sim)
+
+ # 3. Calculate repulsion forces based on artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on pressured radii)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = learning_rate * (1.0 - progress)**2 # Annealing learning rate
+ centers_for_sim += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
+
+ forces = np.zeros_like(centers_for_sim)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation and fine-tuning, evaluate this configuration
+ current_radii_final = compute_max_radii(centers_for_sim, iterations=radius_calc_final_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy()
+ best_radii = current_radii_final.copy()
+
+ # Return the best configuration found
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # Revert to a safe default if no better solution was found
+ final_centers = np.zeros((n, 2))
+ final_centers[:n-1] = base_centers
+ final_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers, iterations=radius_calc_final_iter)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers, iterations=500): # Changed default to 500 for final precision
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
++ # Add an epsilon for dynamic convergence check
++ convergence_epsilon = 1e-7
++
+ for _ in range(iterations): # Use the passed iterations argument
+ updated_in_pass = False
++
++ previous_radii = radii.copy() # Store radii from previous iteration to calculate delta
++
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+- radii[i] = 0.0
+- radii[j] = 0.0
+- updated_in_pass = True
++ if radii[i] > 0 or radii[j] > 0: # Only update if they had positive radii
++ radii[i], radii[j] = 0.0, 0.0
++ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+- if not updated_in_pass:
++ # After one full pass, check for small changes
++ # Max_delta_r calculation reflects the actual changes made to radii in this iteration
++ max_delta_r = np.max(np.abs(radii - previous_radii))
++
++ # Terminate if no significant updates happened AND the max change is below epsilon
++ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f17cb2df8e3929f7484610c2fe60787502e3857
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/main.py
@@ -0,0 +1,250 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers. This version incorporates:
+ - Multi-start initial placement for the 26th circle.
+ - A two-phase force-directed simulation (annealed growth pressure + fine-tuning).
+ - Vectorized wall repulsion for efficiency.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ sim_iter = 1500 # Increased iterations for thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 500 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_calc_sim_iter = 150 # Iterations for radii during simulation (lower for speed).
+ radius_calc_final_iter = 500 # Iterations for final radii calculation (higher for precision).
+
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset points.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+ # Add a small random perturbation to the base grid centers
+ perturbation_strength = 0.005 * spacing # E.g., 5% of grid spacing
+ current_centers[:n-1] += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers[n-1] = np.array(extra_pos)
+
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Work on a copy and ensure bounds
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically.
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers_for_sim)
+
+ # 3. Calculate repulsion forces based on artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on pressured radii)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = learning_rate * (1.0 - progress)**2 # Annealing learning rate
+ centers_for_sim += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
+
+ forces = np.zeros_like(centers_for_sim)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation and fine-tuning, evaluate this configuration
+ current_radii_final = compute_max_radii(centers_for_sim, iterations=radius_calc_final_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy()
+ best_radii = current_radii_final.copy()
+
+ # Return the best configuration found
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # Revert to a safe default if no better solution was found
+ final_centers = np.zeros((n, 2))
+ final_centers[:n-1] = base_centers
+ final_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers, iterations=radius_calc_final_iter)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers, iterations=500): # Changed default to 500 for final precision
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Add an epsilon for dynamic convergence check
+ convergence_epsilon = 1e-7
+
+ for _ in range(iterations): # Use the passed iterations argument
+ updated_in_pass = False
+
+ previous_radii = radii.copy() # Store radii from previous iteration to calculate delta
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0: # Only update if they had positive radii
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # After one full pass, check for small changes
+ # Max_delta_r calculation reflects the actual changes made to radii in this iteration
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+
+ # Terminate if no significant updates happened AND the max change is below epsilon
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce1365f1f4fb5a6f2a7816f72c8f7b172ccb3683
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/original.py
@@ -0,0 +1,236 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+Improved by combining insights from high-scoring prior programs:
+- Optimized initial placement of the 26th circle.
+- Reduced initial perturbation noise.
+- Tuned learning rate for fine-grained adjustments.
+- Increased iterations for radius calculation convergence.
+- Robust handling of very close circle centers in radius calculation.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers. This version incorporates:
+ - Multi-start initial placement for the 26th circle.
+ - A two-phase force-directed simulation (annealed growth pressure + fine-tuning).
+ - Vectorized wall repulsion for efficiency.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ sim_iter = 1500 # Increased iterations for thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 500 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_calc_sim_iter = 150 # Iterations for radii during simulation (lower for speed).
+ radius_calc_final_iter = 500 # Iterations for final radii calculation (higher for precision).
+
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset points.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+ current_centers[n-1] = np.array(extra_pos)
+
+ centers_for_sim = current_centers.copy() # Work on a copy for the simulation
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically.
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers_for_sim)
+
+ # 3. Calculate repulsion forces based on artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on pressured radii)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = learning_rate * (1.0 - progress)**2 # Annealing learning rate
+ centers_for_sim += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = compute_max_radii(centers_for_sim, iterations=radius_calc_sim_iter)
+
+ forces = np.zeros_like(centers_for_sim)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers_for_sim[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers_for_sim[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation and fine-tuning, evaluate this configuration
+ current_radii_final = compute_max_radii(centers_for_sim, iterations=radius_calc_final_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy()
+ best_radii = current_radii_final.copy()
+
+ # Return the best configuration found
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # Revert to a safe default if no better solution was found
+ final_centers = np.zeros((n, 2))
+ final_centers[:n-1] = base_centers
+ final_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers, iterations=radius_calc_final_iter)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers, iterations=500): # Changed default to 500 for final precision
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(iterations): # Use the passed iterations argument
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..14c2b28927c2efb44b4c73108a4d288f4db5bba2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_63/search_replace.txt
@@ -0,0 +1,94 @@
+
+initial_grid_perturbation
+
+
+
+Introduce a small, random perturbation to the initial 5x5 grid centers for each trial.
+The current strategy uses a perfectly rigid 5x5 grid for the first 25 circles, which can lead to local optima due to its inherent symmetry. By adding a small amount of Gaussian noise to these initial grid positions, the simulation starts from slightly different, asymmetrical configurations for each of the `candidate_extra_positions` trials. This broadens the search space, allowing the force-directed simulation to explore more diverse packing arrangements and potentially escape the local optima associated with the exact grid, leading to a higher sum of radii. The standard deviation of the noise is chosen to be small enough not to destroy the grid structure but large enough to introduce meaningful variation. The `np.clip` ensures perturbed centers remain within the unit square.
+
+
+
+<<<<<<< SEARCH
+ current_centers = np.zeros((n, 2))
+ current_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+ current_centers[n-1] = np.array(extra_pos)
+
+ centers_for_sim = current_centers.copy() # Work on a copy for the simulation
+=======
+ current_centers = np.zeros((n, 2))
+ current_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+ # Add a small random perturbation to the base grid centers
+ perturbation_strength = 0.005 * spacing # E.g., 5% of grid spacing
+ current_centers[:n-1] += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers[n-1] = np.array(extra_pos)
+
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Work on a copy and ensure bounds
+>>>>>>> REPLACE
+
+
+
+dynamic_radius_convergence
+
+
+
+Enhance the termination logic of `compute_max_radii` by tracking the maximum absolute change in any individual radius during an iteration. The function will now terminate if this maximum change (`max_delta_r`) falls below a small epsilon (e.g., `1e-7`). This ensures more precise convergence for the radii calculation, allowing the simulation to proceed with more accurate radius estimations. It prevents premature termination due to `updated_in_pass` being false even if very small adjustments are still needed for maximal radii, while also potentially stopping earlier if convergence is swift. The conditional update for `radii[i] > 0 or radii[j] > 0` prevents setting `updated_in_pass = True` unnecessarily if radii were already zero due to co-located centers.
+
+
+
+<<<<<<< SEARCH
+ for _ in range(iterations): # Use the passed iterations argument
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+=======
+ # Add an epsilon for dynamic convergence check
+ convergence_epsilon = 1e-7
+
+ for _ in range(iterations): # Use the passed iterations argument
+ updated_in_pass = False
+
+ previous_radii = radii.copy() # Store radii from previous iteration to calculate delta
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # Robust handling for circles with identical or very close centers
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0: # Only update if they had positive radii
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # After one full pass, check for small changes
+ # Max_delta_r calculation reflects the actual changes made to radii in this iteration
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+
+ # Terminate if no significant updates happened AND the max change is below epsilon
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..3251ce305931fadce1b55ccbbaf75e18ec7a1fa1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/edit.diff
@@ -0,0 +1,330 @@
+--- a/original.py
++++ b/original.py
+@@ -1,223 +1,285 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+- based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
++ by trying multiple initial placements for the 26th circle
++ and refining each using a two-phase force-directed simulation.
++ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+- # Initialize arrays for 26 circles
+ n = 26
+- centers = np.zeros((n, 2))
+-
+- # --- New Initial Placement Strategy ---
+- # Arrange 25 circles in a dense and symmetric 5x5 grid.
+- # This is a much more stable and efficient base than a 6x4 grid.
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+-
+- k = 0
+- for j in range(num_cells_side):
+- for i in range(num_cells_side):
+- centers[k, 0] = (i + 0.5) * spacing
+- centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- # Place the 26th circle in a natural void of the grid (an interstitial site).
+- # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+- # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+- # minimizing local disruption.
+- centers[25] = [spacing, spacing]
+-
+- # Refine the strong initial grid using a force-directed simulation. This allows
+- # the circles to shift from the rigid grid into a more optimal packing.
+- centers = refine_centers_with_simulation(centers)
+-
+- # Compute maximum valid radii for the refined configuration.
+- radii = compute_max_radii(centers)
+- return centers, radii
+-
+-
+-def refine_centers_with_simulation(centers):
+- """
+- Refines circle centers using a two-phase force-directed simulation inspired
+- by high-performance prior implementations. It first uses an annealed 'growth
+- pressure' to explore the solution space, then fine-tunes the positions.
+- """
+- n = centers.shape[0]
+-
+- # --- Hyperparameters from best-performing prior program, tuned for longer simulation ---
++
++ # --- Hyperparameters for the simulation phases ---
+ sim_iter = 2000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 400
+ fine_tune_lr = 0.001
+- radius_sim_iter = 75 # Drastically reduced from 500 for performance
++ radius_sim_iter = 75 # Iterations for compute_max_radii during simulation (for speed)
++ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
++
++ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ base_25_centers = np.zeros((n - 1, 2))
++ k = 0
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ base_25_centers[k, 0] = (i + 0.5) * spacing
++ base_25_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ # Strategic candidate placements for the 26th circle.
++ # Includes interstitial points and corner-offset positions to explore diverse optima.
++ candidate_extra_positions = [
++ [spacing, spacing], # (0.2, 0.2)
++ [spacing, 0.5], # (0.2, 0.5)
++ [spacing, 1 - spacing], # (0.2, 0.8)
++ [0.5, spacing], # (0.5, 0.2)
++ [0.5, 0.5], # (0.5, 0.5) - Center
++ [0.5, 1 - spacing], # (0.5, 0.8)
++ [1 - spacing, spacing], # (0.8, 0.2)
++ [1 - spacing, 0.5], # (0.8, 0.5)
++ [1 - spacing, 1 - spacing], # (0.8, 0.8)
++ [0.05, 0.05], # Corner offsets
++ [0.05, 0.95],
++ [0.95, 0.05],
++ [0.95, 0.95]
++ ]
++
++ best_sum_radii = -1.0
++ best_centers_overall = None
++ best_radii_overall = None
++
++ # Iterate through each candidate position for the 26th circle
++ for extra_pos in candidate_extra_positions:
++ # Create a new 'centers' array for this trial to avoid modifying base_25_centers
++ current_centers_trial = np.zeros((n, 2))
++ current_centers_trial[:n-1] = base_25_centers.copy()
++ current_centers_trial[n-1] = np.array(extra_pos)
++
++ # Refine these centers using the simulation, passing all necessary hyperparameters
++ refined_centers = refine_centers_with_simulation(
++ current_centers_trial,
++ sim_iter, learning_rate, wall_strength,
++ initial_growth_pressure, final_growth_pressure,
++ fine_tune_iter, fine_tune_lr, radius_sim_iter
++ )
++
++ # Compute the final, precise radii for this refined configuration
++ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
++ current_sum_radii = np.sum(current_radii_final)
++
++ # Update if this is the best packing found so far
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_centers_overall = refined_centers.copy()
++ best_radii_overall = current_radii_final.copy()
++
++ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
++ if best_centers_overall is None:
++ # Revert to a safe default if no better solution was found
++ default_centers = np.zeros((n, 2))
++ default_centers[:n-1] = base_25_centers
++ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
++ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
++ return default_centers, default_radii
++ else:
++ return best_centers_overall, best_radii_overall
++
++
++def refine_centers_with_simulation(
++ centers,
++ sim_iter, learning_rate, wall_strength,
++ initial_growth_pressure, final_growth_pressure,
++ fine_tune_iter, fine_tune_lr, radius_sim_iter
++):
++ """
++ Refines circle centers using a two-phase force-directed simulation.
++ It first uses an annealed 'growth pressure' to explore the solution space,
++ then fine-tunes the positions with actual overlaps.
++
++ Args:
++ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
++ sim_iter: Number of iterations for the main simulation phase.
++ learning_rate: Base learning rate for center updates.
++ wall_strength: Strength of repulsion from square walls.
++ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
++ final_growth_pressure: Final multiplier for radii.
++ fine_tune_iter: Number of iterations for the fine-tuning phase.
++ fine_tune_lr: Learning rate for the fine-tuning phase.
++ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
++
++ Returns:
++ np.array: Refined circle centers.
++ """
++ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+ def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # The number of iterations is parameterized for performance tuning.
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..d8df5ad4c2de9e8724c08e572ff599e893251eea
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/main.py
@@ -0,0 +1,285 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases ---
+ sim_iter = 2000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 400
+ fine_tune_lr = 0.001
+ radius_sim_iter = 75 # Iterations for compute_max_radii during simulation (for speed)
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ # Create a new 'centers' array for this trial to avoid modifying base_25_centers
+ current_centers_trial = np.zeros((n, 2))
+ current_centers_trial[:n-1] = base_25_centers.copy()
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # The number of iterations is parameterized for performance tuning.
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f3e6f99d2f72d27c3d8749c3f4c93249a85af6e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/original.py
@@ -0,0 +1,223 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Refine the strong initial grid using a force-directed simulation. This allows
+ # the circles to shift from the rigid grid into a more optimal packing.
+ centers = refine_centers_with_simulation(centers)
+
+ # Compute maximum valid radii for the refined configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def refine_centers_with_simulation(centers):
+ """
+ Refines circle centers using a two-phase force-directed simulation inspired
+ by high-performance prior implementations. It first uses an annealed 'growth
+ pressure' to explore the solution space, then fine-tunes the positions.
+ """
+ n = centers.shape[0]
+
+ # --- Hyperparameters from best-performing prior program, tuned for longer simulation ---
+ sim_iter = 2000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 400
+ fine_tune_lr = 0.001
+ radius_sim_iter = 75 # Drastically reduced from 500 for performance
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # The number of iterations is parameterized for performance tuning.
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c7ea7e88d75cf620fb7e34e46fca6ad796f3b74b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_64/search_replace.txt
@@ -0,0 +1,219 @@
+The current implementation of the circle packing algorithm uses a force-directed simulation initialized with a 5x5 grid and a single, fixed interstitial position for the 26th circle. While the force-directed simulation is good at local optimization, starting from only one initial configuration for the 26th circle can lead to getting stuck in local optima.
+
+The best-performing prior Genetic Algorithm (GA) achieved a sum of radii of 2.27, suggesting that a broader exploration of the initial solution space is beneficial. A previous iteration of a force-directed simulation, also provided in the prompt, utilized a "multi-start" approach by trying several candidate positions for the 26th circle, and this achieved a sum of radii of 1.95, which was comparable to the current single-start force-directed approach (1.96).
+
+The proposed improvement integrates this successful multi-start strategy into the current force-directed simulation. Instead of placing the 26th circle at a single fixed location, `construct_packing` will now iterate through a list of strategic candidate positions for the 26th circle (including various interstitial sites and corner-offset positions). For each candidate position, the full two-phase force-directed refinement simulation will be run. The algorithm will then compare the final sum of radii for all these refined configurations and return the very best one found. This effectively combines the local optimization power of the force-directed simulation with a broader initial search, increasing the chances of finding a better global optimum.
+
+To facilitate this, the `refine_centers_with_simulation` function will be updated to accept its hyperparameters as arguments, making it reusable within the multi-start loop, rather than relying on internal constants.
+
+
+multi_start_refinement
+
+
+
+This enhancement implements a multi-start strategy for the 26th circle's initial placement. Instead of a single fixed starting point, the `construct_packing` function now iterates through a list of strategically chosen candidate positions (various interstitial voids and corner-offset positions) for the 26th circle. For each candidate, the full two-phase force-directed simulation (`refine_centers_with_simulation`) is executed. The refined packing with the highest sum of radii across all trials is then selected as the final result.
+
+This change aims to address the limitation of starting from a single initial configuration, which can easily lead to local optima. By exploring multiple promising starting points, the algorithm has a much better chance of finding a more optimal packing. This approach was inspired by prior programs that achieved higher scores by incorporating more initial diversity.
+
+The `refine_centers_with_simulation` function's signature and internal usage are updated to accept its hyperparameters as arguments, making it re-usable within the new multi-start loop. The number of iterations for radius calculation during simulation (`radius_sim_iter`) remains low for speed, while the final radius calculation uses a higher number of iterations (`final_radius_calc_iter`) for precision.
+
+
+
+<<<<<<< SEARCH
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ based on a 5x5 grid plus one interstitial circle to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 25 circles in a dense and symmetric 5x5 grid.
+ # This is a much more stable and efficient base than a 6x4 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a natural void of the grid (an interstitial site).
+ # The position [spacing, spacing] i.e. [0.2, 0.2] is equidistant from the
+ # four nearest centers at (0.1,0.1), (0.1,0.3), (0.3,0.1), (0.3,0.3),
+ # minimizing local disruption.
+ centers[25] = [spacing, spacing]
+
+ # Refine the strong initial grid using a force-directed simulation. This allows
+ # the circles to shift from the rigid grid into a more optimal packing.
+ centers = refine_centers_with_simulation(centers)
+
+ # Compute maximum valid radii for the refined configuration.
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def refine_centers_with_simulation(centers):
+ """
+ Refines circle centers using a two-phase force-directed simulation inspired
+ by high-performance prior implementations. It first uses an annealed 'growth
+ pressure' to explore the solution space, then fine-tunes the positions.
+ """
+ n = centers.shape[0]
+
+ # --- Hyperparameters from best-performing prior program, tuned for longer simulation ---
+ sim_iter = 2000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 400
+ fine_tune_lr = 0.001
+ radius_sim_iter = 75 # Drastically reduced from 500 for performance
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+=======
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases ---
+ sim_iter = 2000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 400
+ fine_tune_lr = 0.001
+ radius_sim_iter = 75 # Iterations for compute_max_radii during simulation (for speed)
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ # Create a new 'centers' array for this trial to avoid modifying base_25_centers
+ current_centers_trial = np.zeros((n, 2))
+ current_centers_trial[:n-1] = base_25_centers.copy()
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..80a15b5e2463bdd995f95b51b8d2e64664d51060
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..57f55eaebc49c7dc9772914d1dbdfbf30aa62c24
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/edit.diff
@@ -0,0 +1,369 @@
+--- a/original.py
++++ b/original.py
+@@ -1,223 +1,264 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+- def __init__(self, n, config):
++ def __init__(self, initial_centers, config):
+ """Initializes the packer with the number of circles and a config dict."""
+- self.n = n
++ self.n = initial_centers.shape[0]
+ self.config = config
+- self.centers = np.zeros((n, 2))
+- self.radii = np.zeros(n)
+-
+- def _initialize_centers(self):
+- """Sets up the initial circle positions using the best-known static 5x5 grid."""
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for j in range(num_cells_side):
+- for i in range(num_cells_side):
+- self.centers[k, 0] = (i + 0.5) * spacing
+- self.centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+- # Place the 26th circle in the optimal interstitial void found in prior runs.
+- self.centers[25] = [spacing, spacing]
+-
+- def _compute_radii_for_centers(self, centers_to_eval):
++ self.centers = np.array(initial_centers).copy() # Use provided initial centers
++ self.radii = np.zeros(self.n) # Will be filled later
++
++ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
++ n_local = centers_to_eval.shape[0]
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+-
+- for _ in range(self.config['radius_iter']):
++
++ convergence_epsilon = 1e-7 # Define epsilon for dynamic convergence check
++
++ for _ in range(iterations):
+ updated = False
+- for i in range(self.n):
+- for j in range(i + 1, self.n):
++ previous_radii = radii.copy()
++
++ for i in range(n_local):
++ for j in range(i + 1, n_local):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+-
++
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+- radii[i], radii[j] = 0.0, 0.0
++ if radii[i] > 0 or radii[j] > 0:
++ radii[i], radii[j] = 0.0, 0.0
++ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+- updated = True
+- if not updated:
++ updated = True
++
++ max_delta_r = np.max(np.abs(radii - previous_radii))
++ if not updated and max_delta_r < convergence_epsilon: # More robust check for no updates
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
++
++ # Use radius_calc_sim_iter during simulation phases
++ radius_iterations_sim = self.config['radius_calc_sim_iter']
+
+ for i_iter in range(iterations):
+- # Anneal growth pressure quadratically from high to low.
+- # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+- # 1. Calculate the maximum non-overlapping radii for the current positions.
+- current_radii = self._compute_radii_for_centers(self.centers)
+-
+- # 2. Artificially inflate radii to create an expansion 'pressure'.
++ current_radii = self._compute_radii_for_centers(self.centers, radius_iterations_sim) # Pass iterations
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+- # 3. Calculate repulsion forces based on the artificial overlaps.
+- # a) Circle-to-circle repulsion (vectorized)
+- # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+-
+- # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+- # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+- np.fill_diagonal(overlaps, 0) # No self-repulsion
+-
+- # Calculate force magnitudes and apply them along the normalized difference vectors
++ overlaps[overlaps < 0] = 0.0
++ np.fill_diagonal(overlaps, 0)
++
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+-
+- # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+- # b) Wall repulsion (vectorized)
+- # Left wall (x=0)
+- overlap_l = pressured_radii - self.centers[:, 0]
+- forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+- # Right wall (x=1)
+- overlap_r = (self.centers[:, 0] + pressured_radii) - 1.0
+- forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+- # Bottom wall (y=0)
+- overlap_b = pressured_radii - self.centers[:, 1]
+- forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+- # Top wall (y=1)
+- overlap_t = (self.centers[:, 1] + pressured_radii) - 1.0
+- forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+-
+- # 4. Update center positions with a decaying learning rate for stability.
++ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0])
++ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0)
++ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1])
++ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0)
++
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+-
+- # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+- lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
++ lr = self.config['fine_tune_lr']
+ wall_strength = self.config['wall_strength']
++
++ # Use radius_calc_sim_iter during simulation phases
++ radius_iterations_sim = self.config['radius_calc_sim_iter']
+
+ for _ in range(iterations):
+- # 1. Calculate radii for the current positions. NO growth pressure.
+- current_radii = self._compute_radii_for_centers(self.centers)
++ current_radii = self._compute_radii_for_centers(self.centers, radius_iterations_sim) # Pass iterations
+
+ forces = np.zeros_like(self.centers)
+
+- # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+- # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+- # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b) Wall repulsion (vectorized, based on actual radii)
+- # Left wall (x=0)
+- overlap_l = current_radii - self.centers[:, 0]
+- forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+- # Right wall (x=1)
+- overlap_r = (self.centers[:, 0] + current_radii) - 1.0
+- forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+- # Bottom wall (y=0)
+- overlap_b = current_radii - self.centers[:, 1]
+- forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+- # Top wall (y=1)
+- overlap_t = (self.centers[:, 1] + current_radii) - 1.0
+- forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+-
+- # 3. Update positions with small learning rate.
++ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
++ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
++ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
++ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
++
+ self.centers += forces * lr
+-
+- # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+- self._initialize_centers()
++ # Initial centers are already set in __init__
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+- self.radii = self._compute_radii_for_centers(self.centers)
++ self.radii = self._compute_radii_for_centers(self.centers, self.config['radius_calc_final_iter']) # Pass iterations
+ return self.centers, self.radii
+
+ def construct_packing():
+ """
+- Main function to construct the circle packing. This implementation shifts from
+- a procedural script to an object-oriented approach by using the
+- ConfigurableHybridPacker class to manage the packing process.
+- """
+- # Hyperparameters are now centralized in a configuration dictionary.
+- # We introduce an annealing schedule for growth pressure to break out of
+- # the initial grid's local minimum and a final fine-tuning phase.
++ Main function to construct the circle packing. This implementation uses a multi-start
++ strategy with a ConfigurableHybridPacker to refine promising initial configurations.
++ """
++ n = 26
++
+ packer_config = {
+- 'sim_iter': 2000, # Doubled iterations for more thorough refinement.
+- 'radius_iter': 250, # Sufficient iterations for radius convergence.
++ 'sim_iter': 2500, # Increased iterations for more thorough refinement.
++ 'radius_calc_sim_iter': 150, # Iterations for radii during simulation (lower for speed).
++ 'radius_calc_final_iter': 500, # Iterations for final radii calculation (higher for precision).
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+- 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
++ 'initial_growth_pressure': 1.03, # Stronger initial pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+- 'fine_tune_iter': 400, # Doubled iterations for final settlement.
++ 'fine_tune_iter': 600, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+- packer = ConfigurableHybridPacker(n=26, config=packer_config)
+- centers, radii = packer.pack()
+-
+- return centers, radii
++ # Initial 5x5 grid setup for 25 circles
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ base_grid_centers = np.zeros((n - 1, 2)) # 25 circles
++ k = 0
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ base_grid_centers[k, 0] = (i + 0.5) * spacing
++ base_grid_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ # Strategic candidate placements for the 26th circle.
++ # A mix of interstitial, corner-offset, and mid-edge points.
++ candidate_extra_positions_base = [
++ [spacing, spacing], # (0.2, 0.2)
++ [spacing, 0.5], # (0.2, 0.5)
++ [spacing, 1 - spacing], # (0.2, 0.8)
++ [0.5, spacing], # (0.5, 0.2)
++ [0.5, 0.5], # (0.5, 0.5) - Center
++ [0.5, 1 - spacing], # (0.5, 0.8)
++ [1 - spacing, spacing], # (0.8, 0.2)
++ [1 - spacing, 0.5], # (0.8, 0.5)
++ [1 - spacing, 1 - spacing], # (0.8, 0.8)
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
++ [0.05, 0.5], [0.5, 0.05], [0.95, 0.5], [0.5, 0.95], # Edge midpoints
++ # Add a few more slightly varied points near previous strategic ones
++ [spacing * 1.2, spacing * 1.2], # Slightly offset from (0.2, 0.2)
++ [0.5 - spacing/4, 0.5 + spacing/4], # Slightly off-center
++ [1 - spacing * 1.2, 1 - spacing * 1.2] # Slightly offset from (0.8, 0.8)
++ ]
++
++ # Introduce some randomness to the starting positions for greater diversity
++ # For each base position, generate a few perturbed versions
++ expanded_candidate_positions = []
++ num_perturbations_per_base = 2 # Generate N perturbed versions for each base position
++ perturbation_strength_initial_extra = 0.02 # For the 26th circle
++
++ for base_pos in candidate_extra_positions_base:
++ expanded_candidate_positions.append(np.array(base_pos)) # Include the exact base position
++ for _ in range(num_perturbations_per_base - 1): # Generate (N-1) perturbed versions
++ perturbed_pos = np.array(base_pos) + np.random.normal(0, perturbation_strength_initial_extra, 2)
++ expanded_candidate_positions.append(np.clip(perturbed_pos, 0.0, 1.0))
++
++ best_sum_radii = -1.0
++ best_centers = None
++ best_radii = None
++
++ # Iterate through expanded candidate positions for the 26th circle
++ for extra_pos_candidate in expanded_candidate_positions:
++ current_centers = np.zeros((n, 2))
++ current_centers[:n-1] = base_grid_centers.copy() # Use copy to ensure independence
++
++ # Apply a small random perturbation to the base grid centers
++ perturbation_strength_grid = 0.005 * spacing # E.g., 5% of grid spacing
++ current_centers[:n-1] += np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
++
++ # Set the 26th circle's initial position
++ current_centers[n-1] = np.array(extra_pos_candidate)
++
++ # Ensure all initial centers are within bounds
++ initial_centers_clipped = np.clip(current_centers, 0.0, 1.0)
++
++ # Create a new packer instance for each initial configuration
++ packer = ConfigurableHybridPacker(initial_centers_clipped, packer_config)
++
++ # Run the packing process for this configuration
++ centers_after_packing, radii_after_packing = packer.pack()
++
++ current_sum_radii = np.sum(radii_after_packing)
++
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_centers = centers_after_packing.copy()
++ best_radii = radii_after_packing.copy()
++
++ # Fallback if no valid configuration was found (should not happen with good candidates)
++ if best_centers is None:
++ # Default to a simple 5x5 grid plus the first extra position candidate
++ default_centers = np.zeros((n, 2))
++ default_centers[:n-1] = base_grid_centers
++ default_centers[n-1] = expanded_candidate_positions[0]
++ default_centers_clipped = np.clip(default_centers, 0.0, 1.0)
++ default_packer = ConfigurableHybridPacker(default_centers_clipped, packer_config)
++ best_centers, best_radii = default_packer.pack() # Re-run for a guaranteed result
++
++ return best_centers, best_radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..55e367338d20dbca3936b5be63a836a297e242f2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/main.py
@@ -0,0 +1,264 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, initial_centers, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = initial_centers.shape[0]
+ self.config = config
+ self.centers = np.array(initial_centers).copy() # Use provided initial centers
+ self.radii = np.zeros(self.n) # Will be filled later
+
+ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ n_local = centers_to_eval.shape[0]
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-7 # Define epsilon for dynamic convergence check
+
+ for _ in range(iterations):
+ updated = False
+ previous_radii = radii.copy()
+
+ for i in range(n_local):
+ for j in range(i + 1, n_local):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated and max_delta_r < convergence_epsilon: # More robust check for no updates
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ # Use radius_calc_sim_iter during simulation phases
+ radius_iterations_sim = self.config['radius_calc_sim_iter']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_centers(self.centers, radius_iterations_sim) # Pass iterations
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr']
+ wall_strength = self.config['wall_strength']
+
+ # Use radius_calc_sim_iter during simulation phases
+ radius_iterations_sim = self.config['radius_calc_sim_iter']
+
+ for _ in range(iterations):
+ current_radii = self._compute_radii_for_centers(self.centers, radius_iterations_sim) # Pass iterations
+
+ forces = np.zeros_like(self.centers)
+
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+
+ self.centers += forces * lr
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ # Initial centers are already set in __init__
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers, self.config['radius_calc_final_iter']) # Pass iterations
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation uses a multi-start
+ strategy with a ConfigurableHybridPacker to refine promising initial configurations.
+ """
+ n = 26
+
+ packer_config = {
+ 'sim_iter': 2500, # Increased iterations for more thorough refinement.
+ 'radius_calc_sim_iter': 150, # Iterations for radii during simulation (lower for speed).
+ 'radius_calc_final_iter': 500, # Iterations for final radii calculation (higher for precision).
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.03, # Stronger initial pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 600, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers[k, 0] = (i + 0.5) * spacing
+ base_grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # A mix of interstitial, corner-offset, and mid-edge points.
+ candidate_extra_positions_base = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.05, 0.5], [0.5, 0.05], [0.95, 0.5], [0.5, 0.95], # Edge midpoints
+ # Add a few more slightly varied points near previous strategic ones
+ [spacing * 1.2, spacing * 1.2], # Slightly offset from (0.2, 0.2)
+ [0.5 - spacing/4, 0.5 + spacing/4], # Slightly off-center
+ [1 - spacing * 1.2, 1 - spacing * 1.2] # Slightly offset from (0.8, 0.8)
+ ]
+
+ # Introduce some randomness to the starting positions for greater diversity
+ # For each base position, generate a few perturbed versions
+ expanded_candidate_positions = []
+ num_perturbations_per_base = 2 # Generate N perturbed versions for each base position
+ perturbation_strength_initial_extra = 0.02 # For the 26th circle
+
+ for base_pos in candidate_extra_positions_base:
+ expanded_candidate_positions.append(np.array(base_pos)) # Include the exact base position
+ for _ in range(num_perturbations_per_base - 1): # Generate (N-1) perturbed versions
+ perturbed_pos = np.array(base_pos) + np.random.normal(0, perturbation_strength_initial_extra, 2)
+ expanded_candidate_positions.append(np.clip(perturbed_pos, 0.0, 1.0))
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through expanded candidate positions for the 26th circle
+ for extra_pos_candidate in expanded_candidate_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:n-1] = base_grid_centers.copy() # Use copy to ensure independence
+
+ # Apply a small random perturbation to the base grid centers
+ perturbation_strength_grid = 0.005 * spacing # E.g., 5% of grid spacing
+ current_centers[:n-1] += np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+
+ # Set the 26th circle's initial position
+ current_centers[n-1] = np.array(extra_pos_candidate)
+
+ # Ensure all initial centers are within bounds
+ initial_centers_clipped = np.clip(current_centers, 0.0, 1.0)
+
+ # Create a new packer instance for each initial configuration
+ packer = ConfigurableHybridPacker(initial_centers_clipped, packer_config)
+
+ # Run the packing process for this configuration
+ centers_after_packing, radii_after_packing = packer.pack()
+
+ current_sum_radii = np.sum(radii_after_packing)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_after_packing.copy()
+ best_radii = radii_after_packing.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # Default to a simple 5x5 grid plus the first extra position candidate
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_grid_centers
+ default_centers[n-1] = expanded_candidate_positions[0]
+ default_centers_clipped = np.clip(default_centers, 0.0, 1.0)
+ default_packer = ConfigurableHybridPacker(default_centers_clipped, packer_config)
+ best_centers, best_radii = default_packer.pack() # Re-run for a guaranteed result
+
+ return best_centers, best_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..4d61b633806ab6c86af21ff700f268c84f806ce7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/original.py
@@ -0,0 +1,223 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized)
+ # Left wall (x=0)
+ overlap_l = pressured_radii - self.centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (self.centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - self.centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (self.centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - self.centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (self.centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - self.centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (self.centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary.
+ # We introduce an annealing schedule for growth pressure to break out of
+ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+ 'sim_iter': 2000, # Doubled iterations for more thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 400, # Doubled iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..46b370fd97fcfc699bc4f093e63b6e263e6820c4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results
+Run 1/1 completed in 309.32 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9768620637166716
+ public: {'centers_str': ' centers[0] = (0.0782, 0.0822)\n centers[1] = (0.3287, 0.0687)\n centers[2] = (0.4999, 0.0989)\n centers[3] = (0.6997, 0.0998)\n centers[4] = (0.8978, 0.1020)\n centers[5] = (0.0787, 0.3206)\n centers[6] = (0.3188, 0.3246)\n centers[7] = (0.4983, 0.2996)\n centers[8] = (0.6990, 0.3000)\n centers[9] = (0.9007, 0.3002)\n centers[10] = (0.0986, 0.5013)\n centers[11] = (0.2999, 0.5007)\n centers[12] = (0.5012, 0.4999)\n centers[13] = (0.6978, 0.4723)\n centers[14] = (0.8973, 0.4983)\n centers[15] = (0.1007, 0.6985)\n centers[16] = (0.2701, 0.6868)\n centers[17] = (0.4583, 0.6850)\n centers[18] = (0.7151, 0.6846)\n centers[19] = (0.9319, 0.7018)\n centers[20] = (0.0884, 0.9022)\n centers[21] = (0.2930, 0.8833)\n centers[22] = (0.5177, 0.8924)\n centers[23] = (0.7076, 0.9170)\n centers[24] = (0.8950, 0.8949)\n centers[25] = (0.2167, 0.1951)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9768620637166716}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/packing_viz.png
+ execution_time_mean: 309.3191651580855
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..23f5e3bf59668105638fa7a43a75bf64f206159d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9768620637166716,
+ "public": {
+ "centers_str": " centers[0] = (0.0782, 0.0822)\n centers[1] = (0.3287, 0.0687)\n centers[2] = (0.4999, 0.0989)\n centers[3] = (0.6997, 0.0998)\n centers[4] = (0.8978, 0.1020)\n centers[5] = (0.0787, 0.3206)\n centers[6] = (0.3188, 0.3246)\n centers[7] = (0.4983, 0.2996)\n centers[8] = (0.6990, 0.3000)\n centers[9] = (0.9007, 0.3002)\n centers[10] = (0.0986, 0.5013)\n centers[11] = (0.2999, 0.5007)\n centers[12] = (0.5012, 0.4999)\n centers[13] = (0.6978, 0.4723)\n centers[14] = (0.8973, 0.4983)\n centers[15] = (0.1007, 0.6985)\n centers[16] = (0.2701, 0.6868)\n centers[17] = (0.4583, 0.6850)\n centers[18] = (0.7151, 0.6846)\n centers[19] = (0.9319, 0.7018)\n centers[20] = (0.0884, 0.9022)\n centers[21] = (0.2930, 0.8833)\n centers[22] = (0.5177, 0.8924)\n centers[23] = (0.7076, 0.9170)\n centers[24] = (0.8950, 0.8949)\n centers[25] = (0.2167, 0.1951)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9768620637166716
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/results/packing_viz.png",
+ "execution_time_mean": 309.3191651580855,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d2b499376213be710336ff403b090ca87eb06dd6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_65/rewrite.txt
@@ -0,0 +1,255 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, initial_centers, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = initial_centers.shape[0]
+ self.config = config
+ self.centers = np.array(initial_centers).copy() # Use provided initial centers
+ self.radii = np.zeros(self.n) # Will be filled later
+
+ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ n_local = centers_to_eval.shape[0]
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-7 # Define epsilon for dynamic convergence check
+
+ for _ in range(iterations):
+ updated = False
+ previous_radii = radii.copy()
+
+ for i in range(n_local):
+ for j in range(i + 1, n_local):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated and max_delta_r < convergence_epsilon: # More robust check for no updates
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ # Use radius_calc_sim_iter during simulation phases
+ radius_iterations_sim = self.config['radius_calc_sim_iter']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_centers(self.centers, radius_iterations_sim) # Pass iterations
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr']
+ wall_strength = self.config['wall_strength']
+
+ # Use radius_calc_sim_iter during simulation phases
+ radius_iterations_sim = self.config['radius_calc_sim_iter']
+
+ for _ in range(iterations):
+ current_radii = self._compute_radii_for_centers(self.centers, radius_iterations_sim) # Pass iterations
+
+ forces = np.zeros_like(self.centers)
+
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+
+ self.centers += forces * lr
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ # Initial centers are already set in __init__
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers, self.config['radius_calc_final_iter']) # Pass iterations
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation uses a multi-start
+ strategy with a ConfigurableHybridPacker to refine promising initial configurations.
+ """
+ n = 26
+
+ packer_config = {
+ 'sim_iter': 2500, # Increased iterations for more thorough refinement.
+ 'radius_calc_sim_iter': 150, # Iterations for radii during simulation (lower for speed).
+ 'radius_calc_final_iter': 500, # Iterations for final radii calculation (higher for precision).
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.03, # Stronger initial pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 600, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers[k, 0] = (i + 0.5) * spacing
+ base_grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # A mix of interstitial, corner-offset, and mid-edge points.
+ candidate_extra_positions_base = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.05, 0.5], [0.5, 0.05], [0.95, 0.5], [0.5, 0.95], # Edge midpoints
+ # Add a few more slightly varied points near previous strategic ones
+ [spacing * 1.2, spacing * 1.2], # Slightly offset from (0.2, 0.2)
+ [0.5 - spacing/4, 0.5 + spacing/4], # Slightly off-center
+ [1 - spacing * 1.2, 1 - spacing * 1.2] # Slightly offset from (0.8, 0.8)
+ ]
+
+ # Introduce some randomness to the starting positions for greater diversity
+ # For each base position, generate a few perturbed versions
+ expanded_candidate_positions = []
+ num_perturbations_per_base = 2 # Generate N perturbed versions for each base position
+ perturbation_strength_initial_extra = 0.02 # For the 26th circle
+
+ for base_pos in candidate_extra_positions_base:
+ expanded_candidate_positions.append(np.array(base_pos)) # Include the exact base position
+ for _ in range(num_perturbations_per_base - 1): # Generate (N-1) perturbed versions
+ perturbed_pos = np.array(base_pos) + np.random.normal(0, perturbation_strength_initial_extra, 2)
+ expanded_candidate_positions.append(np.clip(perturbed_pos, 0.0, 1.0))
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through expanded candidate positions for the 26th circle
+ for extra_pos_candidate in expanded_candidate_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:n-1] = base_grid_centers.copy() # Use copy to ensure independence
+
+ # Apply a small random perturbation to the base grid centers
+ perturbation_strength_grid = 0.005 * spacing # E.g., 5% of grid spacing
+ current_centers[:n-1] += np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+
+ # Set the 26th circle's initial position
+ current_centers[n-1] = np.array(extra_pos_candidate)
+
+ # Ensure all initial centers are within bounds
+ initial_centers_clipped = np.clip(current_centers, 0.0, 1.0)
+
+ # Create a new packer instance for each initial configuration
+ packer = ConfigurableHybridPacker(initial_centers_clipped, packer_config)
+
+ # Run the packing process for this configuration
+ centers_after_packing, radii_after_packing = packer.pack()
+
+ current_sum_radii = np.sum(radii_after_packing)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_after_packing.copy()
+ best_radii = radii_after_packing.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # Default to a simple 5x5 grid plus the first extra position candidate
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_grid_centers
+ default_centers[n-1] = expanded_candidate_positions[0]
+ default_centers_clipped = np.clip(default_centers, 0.0, 1.0)
+ default_packer = ConfigurableHybridPacker(default_centers_clipped, packer_config)
+ best_centers, best_radii = default_packer.pack() # Re-run for a guaranteed result
+
+ return best_centers, best_radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..99c198bd9c29bc45b35b23824e0301763466ad10
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..d80e662d4680d2a355a3e1e895d72be9b2ae9c08
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/edit.diff
@@ -0,0 +1,408 @@
+--- a/original.py
++++ b/original.py
+@@ -1,197 +1,234 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This uses an iterative proportional scaling method.
++def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
++ """
++ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+- radii = np.ones(n)
+-
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Iterations for the radii calculation, for robust convergence
+- # This part of the code is intentionally kept as-is, as it's a core utility function
+- # that is not part of the GA's 'evolutionary' algorithm itself, but rather its fitness evaluation.
+- for _ in range(500):
+- updated_in_pass = False
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ for _ in range(max_iter):
++ old_radii = radii.copy()
++ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9: # Centers are practically identical
+- # To prevent division by zero and ensure no overlap, assign zero radius
+- radii[i] = 0.0
+- radii[j] = 0.0
+- updated_in_pass = True
+- continue
+-
+ if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- if not updated_in_pass:
++ if dist < 1e-9:
++ radii[i], radii[j] = 0.0, 0.0
++ else:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++
++ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+-
+- return np.maximum(radii, 0.0) # Ensure no negative radii
++
++ return np.maximum(radii, 0)
++
++
++class LocalSearchRefiner:
++ """A class to perform fast, local refinement on a set of centers."""
++ def __init__(self, iterations, learning_rate, wall_strength):
++ self.iterations = iterations
++ self.lr = learning_rate
++ self.wall_strength = wall_strength
++
++ def refine(self, centers):
++ """Applies a few steps of force-directed refinement to polish a solution."""
++ refined_centers = centers.copy()
++ for _ in range(self.iterations):
++ # Use faster radius calculation for local search
++ radii = compute_max_radii(refined_centers, max_iter=100)
++
++ # --- Vectorized Force Calculation (no growth pressure) ---
++ # Circle-to-circle forces
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ force_mags = overlaps
++ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ # Wall forces
++ wall_forces = np.zeros_like(refined_centers)
++ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
++ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++
++ refined_centers += forces * self.lr
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ return refined_centers
++
++
++class MemeticAlgorithm:
++ """Encapsulates the entire Memetic Algorithm for circle packing."""
++ def __init__(self, n, config):
++ self.n = n
++ self.config = config
++ self.population = []
++ self.fitnesses = np.array([])
++ self.best_solution = None
++ self.best_fitness = -1.0
++ self.local_search_refiner = LocalSearchRefiner(
++ iterations=config['ls_iter'],
++ learning_rate=config['ls_lr'],
++ wall_strength=config['ls_wall_strength']
++ )
++
++ def _initialize_population(self):
++ """Initializes a diverse population with both random and grid-based individuals."""
++ num_grid_based = self.config['population_size'] // 4
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ interstitial_points = [
++ [spacing, spacing], [spacing, 1 - spacing],
++ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
++ ]
++
++ for i in range(self.config['population_size']):
++ if i < num_grid_based:
++ centers = np.zeros((self.n, 2))
++ k = 0
++ for row in range(num_cells_side):
++ for col in range(num_cells_side):
++ centers[k, 0] = (col + 0.5) * spacing
++ centers[k, 1] = (row + 0.5) * spacing
++ k += 1
++ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
++
++ # Place 26th circle in one of the strategic voids
++ extra_pos = interstitial_points[i % len(interstitial_points)]
++ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
++ self.population.append(np.clip(centers, 0.0, 1.0))
++ else:
++ self.population.append(np.random.rand(self.n, 2))
++
++ def _evaluate_population(self):
++ """Calculates fitness for the population and updates the best solution."""
++ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
++ best_idx = np.argmax(self.fitnesses)
++ if self.fitnesses[best_idx] > self.best_fitness:
++ self.best_fitness = self.fitnesses[best_idx]
++ self.best_solution = self.population[best_idx].copy()
++
++ def _select_parent(self):
++ """Selects a parent using tournament selection."""
++ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
++ tourn_fitnesses = self.fitnesses[tourn_indices]
++ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
++ return self.population[winner_idx]
++
++ def _crossover(self, p1, p2):
++ """Performs uniform crossover, taking whole circles from either parent."""
++ child = p1.copy()
++ mask = np.random.rand(self.n) < 0.5
++ child[mask] = p2[mask]
++ return child
++
++ def _mutate(self, individual, strength):
++ """Applies Gaussian mutation and a chance of a strong mutation."""
++ mutated_ind = individual.copy()
++ for i in range(self.n):
++ if np.random.rand() < self.config['mutation_rate']:
++ noise = np.random.normal(0, strength, size=2)
++ mutated_ind[i] += noise
++
++ if np.random.rand() < 0.02: # 2% chance of a jump mutation
++ idx_to_reset = np.random.randint(0, self.n)
++ mutated_ind[idx_to_reset] = np.random.rand(2)
++
++ return np.clip(mutated_ind, 0.0, 1.0)
++
++ def run(self):
++ """Executes the full memetic algorithm evolution."""
++ self._initialize_population()
++
++ for gen in range(self.config['generations']):
++ self._evaluate_population()
++
++ new_population = []
++
++ # Elitism
++ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
++ for idx in elite_indices:
++ new_population.append(self.population[idx].copy())
++
++ # Anneal mutation strength
++ mut_strength = self.config['mut_strength_end'] + \
++ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
++ (1.0 - (gen / self.config['generations']))**2.0
++
++ while len(new_population) < self.config['population_size']:
++ p1 = self._select_parent()
++ p2 = self._select_parent()
++
++ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
++ child = self._mutate(child, mut_strength)
++
++ # Memetic step: Apply local search probabilistically
++ if np.random.rand() < self.config['local_search_prob']:
++ child = self.local_search_refiner.refine(child)
++
++ new_population.append(child)
++
++ self.population = new_population
++
++ # Final evaluation to get the best of the last generation
++ self._evaluate_population()
++ return self.best_solution
+
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles using a Genetic Algorithm.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
++ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+-
+- # --- Genetic Algorithm Parameters ---
+- POPULATION_SIZE = 100
+- GENERATIONS = 500
+- MUTATION_RATE = 0.2 # Probability of a circle's coordinates being mutated
+- CROSSOVER_RATE = 0.8 # Probability of two parents undergoing crossover
+- MUTATION_STRENGTH_START = 0.1 # Initial std dev for Gaussian mutation
+- MUTATION_STRENGTH_END = 0.005 # Final std dev for Gaussian mutation (annealed)
+- ELITE_COUNT = 5 # Number of best individuals to carry over directly
+- TOURNAMENT_SIZE = 5 # Number of individuals in a tournament for parent selection
+-
+- # --- Initialization ---
+- population = []
+- for i in range(POPULATION_SIZE):
+- if i < POPULATION_SIZE // 4: # Some individuals start from a perturbed grid
+- # 5x5 grid for 25 circles
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- initial_centers = np.zeros((n, 2))
+- k = 0
+- for row in range(num_cells_side):
+- for col in range(num_cells_side):
+- initial_centers[k, 0] = (col + 0.5) * spacing
+- initial_centers[k, 1] = (row + 0.5) * spacing
+- k += 1
+- # Perturb the grid slightly
+- initial_centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+- initial_centers[:25, :] = np.clip(initial_centers[:25, :], 0.0, 1.0)
+- # Place the 26th circle at an interstitial point for initial diversity, with small perturbation
+- if n > 25:
+- initial_centers[25] = [spacing, spacing] + np.random.normal(0, spacing * 0.02, size=2)
+- initial_centers[25] = np.clip(initial_centers[25], 0.0, 1.0)
+- population.append(initial_centers)
+- else: # Most individuals start completely randomly for broader exploration
+- population.append(np.random.rand(n, 2))
+-
+-
+- best_individual_centers = None
+- best_overall_fitness = -1.0
+-
+- # --- Main Genetic Algorithm Loop ---
+- for generation in range(GENERATIONS):
+- # Calculate fitness for each individual
+- fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in population])
+-
+- # Track the best individual in the current population
+- current_best_idx = np.argmax(fitnesses)
+- current_best_fitness = fitnesses[current_best_idx]
+-
+- if current_best_fitness > best_overall_fitness:
+- best_overall_fitness = current_best_fitness
+- best_individual_centers = population[current_best_idx].copy()
+-
+- # Create the next generation
+- new_population = []
+-
+- # Elitism: Carry over the best individuals directly
+- elite_indices = np.argsort(fitnesses)[-ELITE_COUNT:]
+- for idx in elite_indices:
+- new_population.append(population[idx].copy())
+-
+- # Anneal mutation strength for exploration-exploitation trade-off
+- # Cubic decay ensures faster reduction of mutation strength towards fine-tuning
+- mutation_strength = MUTATION_STRENGTH_END + \
+- (MUTATION_STRENGTH_START - MUTATION_STRENGTH_END) * \
+- (1.0 - (generation / GENERATIONS))**1.5
+-
+- # Fill the rest of the new population
+- while len(new_population) < POPULATION_SIZE:
+- # Selection: Choose two parents using tournament selection
+- parent1 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+- parent2 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+-
+- # Crossover: Combine genetic material from parents
+- if np.random.rand() < CROSSOVER_RATE:
+- child = arithmetic_crossover(parent1, parent2)
+- else: # If no crossover, one parent (e.g., parent1) becomes the child
+- child = parent1.copy()
+-
+- # Mutation: Introduce random changes
+- child = gaussian_mutate(child, MUTATION_RATE, mutation_strength)
+-
+- new_population.append(child)
+-
+- population = new_population
+-
+- # After all generations, return the best found individual
+- final_centers = best_individual_centers
+- final_radii = compute_max_radii(final_centers)
+-
+- return final_centers, final_radii
+-
+-
+-def select_parent(population, fitnesses, tournament_size):
+- """
+- Selects a parent using tournament selection.
+- """
+- tournament_indices = np.random.choice(len(population), size=tournament_size, replace=False)
+- tournament_fitnesses = fitnesses[tournament_indices]
+- winner_index_in_tournament = np.argmax(tournament_fitnesses)
+- winner_original_index = tournament_indices[winner_index_in_tournament]
+- return population[winner_original_index]
+-
+-def arithmetic_crossover(parent1, parent2):
+- """
+- Performs arithmetic crossover for continuous variables.
+- Child = alpha * Parent1 + (1-alpha) * Parent2
+- """
+- alpha = np.random.uniform(0.3, 0.7) # Blend factor for averaging parents
+- child = alpha * parent1 + (1 - alpha) * parent2
+- return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+-
+-def gaussian_mutate(individual, mutation_rate, mutation_strength):
+- """
+- Applies Gaussian mutation to an individual's circle centers.
+- Each circle's coordinates have a `mutation_rate` chance of being perturbed.
+- """
+- mutated_individual = individual.copy()
+- num_circles = individual.shape[0]
+-
+- # Iterate through each circle and apply mutation based on mutation_rate
+- for i in range(num_circles):
+- if np.random.rand() < mutation_rate:
+- mutated_individual[i, 0] += np.random.normal(0, mutation_strength)
+- mutated_individual[i, 1] += np.random.normal(0, mutation_strength)
+-
+- # Introduce a small chance for a "strong" mutation (random reset)
+- # This helps escape local optima by completely changing a circle's position
+- if np.random.rand() < 0.01: # 1% chance for a strong mutation
+- idx_to_reset = np.random.randint(0, num_circles)
+- mutated_individual[idx_to_reset, :] = np.random.rand(2) # New random position
+-
+- return np.clip(mutated_individual, 0.0, 1.0)
++ config = {
++ 'population_size': 60,
++ 'generations': 300,
++ 'elite_count': 4,
++ 'tournament_size': 5,
++ 'mutation_rate': 0.3,
++ 'mut_strength_start': 0.1,
++ 'mut_strength_end': 0.001,
++ 'crossover_rate': 0.9,
++ # Memetic Parameters
++ 'local_search_prob': 0.15, # Probability of applying local search to a new child
++ 'ls_iter': 30, # Iterations for the local search refinement
++ 'ls_lr': 0.003, # Learning rate for the local search
++ 'ls_wall_strength': 0.2,
++ }
++
++ solver = MemeticAlgorithm(n=n, config=config)
++ best_centers = solver.run()
++
++ # Final, high-precision radius calculation for the best solution
++ final_radii = compute_max_radii(best_centers, max_iter=1500)
++
++ return best_centers, final_radii
++
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ba4e2c7d8338dadbfc54304e38f172c575e792e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/main.py
@@ -0,0 +1,234 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers."""
+ def __init__(self, iterations, learning_rate, wall_strength):
+ self.iterations = iterations
+ self.lr = learning_rate
+ self.wall_strength = wall_strength
+
+ def refine(self, centers):
+ """Applies a few steps of force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ for _ in range(self.iterations):
+ # Use faster radius calculation for local search
+ radii = compute_max_radii(refined_centers, max_iter=100)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # Circle-to-circle forces
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(
+ iterations=config['ls_iter'],
+ learning_rate=config['ls_lr'],
+ wall_strength=config['ls_wall_strength']
+ )
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 60,
+ 'generations': 300,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.3,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+ 'ls_iter': 30, # Iterations for the local search refinement
+ 'ls_lr': 0.003, # Learning rate for the local search
+ 'ls_wall_strength': 0.2,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..30f34f3a8f19e8ccec64b3ac3a0830432b7c4aca
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/original.py
@@ -0,0 +1,197 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Iterations for the radii calculation, for robust convergence
+ # This part of the code is intentionally kept as-is, as it's a core utility function
+ # that is not part of the GA's 'evolutionary' algorithm itself, but rather its fitness evaluation.
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ # To prevent division by zero and ensure no overlap, assign zero radius
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Genetic Algorithm.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Genetic Algorithm Parameters ---
+ POPULATION_SIZE = 100
+ GENERATIONS = 500
+ MUTATION_RATE = 0.2 # Probability of a circle's coordinates being mutated
+ CROSSOVER_RATE = 0.8 # Probability of two parents undergoing crossover
+ MUTATION_STRENGTH_START = 0.1 # Initial std dev for Gaussian mutation
+ MUTATION_STRENGTH_END = 0.005 # Final std dev for Gaussian mutation (annealed)
+ ELITE_COUNT = 5 # Number of best individuals to carry over directly
+ TOURNAMENT_SIZE = 5 # Number of individuals in a tournament for parent selection
+
+ # --- Initialization ---
+ population = []
+ for i in range(POPULATION_SIZE):
+ if i < POPULATION_SIZE // 4: # Some individuals start from a perturbed grid
+ # 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ initial_centers = np.zeros((n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ # Perturb the grid slightly
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+ initial_centers[:25, :] = np.clip(initial_centers[:25, :], 0.0, 1.0)
+ # Place the 26th circle at an interstitial point for initial diversity, with small perturbation
+ if n > 25:
+ initial_centers[25] = [spacing, spacing] + np.random.normal(0, spacing * 0.02, size=2)
+ initial_centers[25] = np.clip(initial_centers[25], 0.0, 1.0)
+ population.append(initial_centers)
+ else: # Most individuals start completely randomly for broader exploration
+ population.append(np.random.rand(n, 2))
+
+
+ best_individual_centers = None
+ best_overall_fitness = -1.0
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(GENERATIONS):
+ # Calculate fitness for each individual
+ fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in population])
+
+ # Track the best individual in the current population
+ current_best_idx = np.argmax(fitnesses)
+ current_best_fitness = fitnesses[current_best_idx]
+
+ if current_best_fitness > best_overall_fitness:
+ best_overall_fitness = current_best_fitness
+ best_individual_centers = population[current_best_idx].copy()
+
+ # Create the next generation
+ new_population = []
+
+ # Elitism: Carry over the best individuals directly
+ elite_indices = np.argsort(fitnesses)[-ELITE_COUNT:]
+ for idx in elite_indices:
+ new_population.append(population[idx].copy())
+
+ # Anneal mutation strength for exploration-exploitation trade-off
+ # Cubic decay ensures faster reduction of mutation strength towards fine-tuning
+ mutation_strength = MUTATION_STRENGTH_END + \
+ (MUTATION_STRENGTH_START - MUTATION_STRENGTH_END) * \
+ (1.0 - (generation / GENERATIONS))**1.5
+
+ # Fill the rest of the new population
+ while len(new_population) < POPULATION_SIZE:
+ # Selection: Choose two parents using tournament selection
+ parent1 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+ parent2 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+
+ # Crossover: Combine genetic material from parents
+ if np.random.rand() < CROSSOVER_RATE:
+ child = arithmetic_crossover(parent1, parent2)
+ else: # If no crossover, one parent (e.g., parent1) becomes the child
+ child = parent1.copy()
+
+ # Mutation: Introduce random changes
+ child = gaussian_mutate(child, MUTATION_RATE, mutation_strength)
+
+ new_population.append(child)
+
+ population = new_population
+
+ # After all generations, return the best found individual
+ final_centers = best_individual_centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def select_parent(population, fitnesses, tournament_size):
+ """
+ Selects a parent using tournament selection.
+ """
+ tournament_indices = np.random.choice(len(population), size=tournament_size, replace=False)
+ tournament_fitnesses = fitnesses[tournament_indices]
+ winner_index_in_tournament = np.argmax(tournament_fitnesses)
+ winner_original_index = tournament_indices[winner_index_in_tournament]
+ return population[winner_original_index]
+
+def arithmetic_crossover(parent1, parent2):
+ """
+ Performs arithmetic crossover for continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2
+ """
+ alpha = np.random.uniform(0.3, 0.7) # Blend factor for averaging parents
+ child = alpha * parent1 + (1 - alpha) * parent2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+def gaussian_mutate(individual, mutation_rate, mutation_strength):
+ """
+ Applies Gaussian mutation to an individual's circle centers.
+ Each circle's coordinates have a `mutation_rate` chance of being perturbed.
+ """
+ mutated_individual = individual.copy()
+ num_circles = individual.shape[0]
+
+ # Iterate through each circle and apply mutation based on mutation_rate
+ for i in range(num_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_individual[i, 0] += np.random.normal(0, mutation_strength)
+ mutated_individual[i, 1] += np.random.normal(0, mutation_strength)
+
+ # Introduce a small chance for a "strong" mutation (random reset)
+ # This helps escape local optima by completely changing a circle's position
+ if np.random.rand() < 0.01: # 1% chance for a strong mutation
+ idx_to_reset = np.random.randint(0, num_circles)
+ mutated_individual[idx_to_reset, :] = np.random.rand(2) # New random position
+
+ return np.clip(mutated_individual, 0.0, 1.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..96e787062b263dfee82c21f51549ee283f8b9477
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results
+Run 1/1 completed in 204.91 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.3227258290218016
+ public: {'centers_str': ' centers[0] = (0.0968, 0.0974)\n centers[1] = (0.5526, 0.5664)\n centers[2] = (0.5079, 0.0786)\n centers[3] = (0.6801, 0.0933)\n centers[4] = (0.8843, 0.1152)\n centers[5] = (0.0777, 0.2735)\n centers[6] = (0.2658, 0.3395)\n centers[7] = (0.4564, 0.3007)\n centers[8] = (0.7017, 0.3068)\n centers[9] = (0.9244, 0.3022)\n centers[10] = (0.1096, 0.4606)\n centers[11] = (0.3592, 0.5505)\n centers[12] = (0.5390, 0.4742)\n centers[13] = (0.6595, 0.5579)\n centers[14] = (0.8854, 0.4888)\n centers[15] = (0.0936, 0.6669)\n centers[16] = (0.2966, 0.7199)\n centers[17] = (0.4971, 0.6447)\n centers[18] = (0.6892, 0.7098)\n centers[19] = (0.9005, 0.7017)\n centers[20] = (0.1212, 0.8791)\n centers[21] = (0.3200, 0.9186)\n centers[22] = (0.5054, 0.8852)\n centers[23] = (0.7068, 0.9080)\n centers[24] = (0.8976, 0.9003)\n centers[25] = (0.3044, 0.1272)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.3227258290218016}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/packing_viz.png
+ execution_time_mean: 204.91105637094006
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..cbef7502b3ae157b1392b1554f1f0d2cc2eb58b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.3227258290218016,
+ "public": {
+ "centers_str": " centers[0] = (0.0968, 0.0974)\n centers[1] = (0.5526, 0.5664)\n centers[2] = (0.5079, 0.0786)\n centers[3] = (0.6801, 0.0933)\n centers[4] = (0.8843, 0.1152)\n centers[5] = (0.0777, 0.2735)\n centers[6] = (0.2658, 0.3395)\n centers[7] = (0.4564, 0.3007)\n centers[8] = (0.7017, 0.3068)\n centers[9] = (0.9244, 0.3022)\n centers[10] = (0.1096, 0.4606)\n centers[11] = (0.3592, 0.5505)\n centers[12] = (0.5390, 0.4742)\n centers[13] = (0.6595, 0.5579)\n centers[14] = (0.8854, 0.4888)\n centers[15] = (0.0936, 0.6669)\n centers[16] = (0.2966, 0.7199)\n centers[17] = (0.4971, 0.6447)\n centers[18] = (0.6892, 0.7098)\n centers[19] = (0.9005, 0.7017)\n centers[20] = (0.1212, 0.8791)\n centers[21] = (0.3200, 0.9186)\n centers[22] = (0.5054, 0.8852)\n centers[23] = (0.7068, 0.9080)\n centers[24] = (0.8976, 0.9003)\n centers[25] = (0.3044, 0.1272)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.3227258290218016
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/results/packing_viz.png",
+ "execution_time_mean": 204.91105637094006,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7ccc51eebe996fc814c3db1fa374634c72b337de
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_66/rewrite.txt
@@ -0,0 +1,225 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers."""
+ def __init__(self, iterations, learning_rate, wall_strength):
+ self.iterations = iterations
+ self.lr = learning_rate
+ self.wall_strength = wall_strength
+
+ def refine(self, centers):
+ """Applies a few steps of force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ for _ in range(self.iterations):
+ # Use faster radius calculation for local search
+ radii = compute_max_radii(refined_centers, max_iter=100)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # Circle-to-circle forces
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(
+ iterations=config['ls_iter'],
+ learning_rate=config['ls_lr'],
+ wall_strength=config['ls_wall_strength']
+ )
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 60,
+ 'generations': 300,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.3,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+ 'ls_iter': 30, # Iterations for the local search refinement
+ 'ls_lr': 0.003, # Learning rate for the local search
+ 'ls_wall_strength': 0.2,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..d4c507a11db6e0e6c79f4c11517cec6562c8db5d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/edit.diff
@@ -0,0 +1,334 @@
+--- a/original.py
++++ b/original.py
+@@ -1,309 +1,288 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+- # Tuned based on high-scoring prior programs.
+- simulation_iterations = 1000 # Increased iterations for thorough refinement.
++ # Tuned for more aggressive exploration and longer refinement.
++ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+- initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
++ initial_growth_pressure = 1.04 # More aggressive initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+- fine_tune_iter = 200 # Increased iterations for final settlement.
++ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
++
++ # Add a small random perturbation to the base grid to break symmetry
++ perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
++ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
++
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+- centers_for_sim = current_centers.copy() # Operate on a copy for simulation
++ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b. Wall repulsion forces (based on pressured radii)
+- for i in range(n):
+- # Left wall
+- overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
++ # b. Wall repulsion forces (vectorized, based on pressured radii)
++ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
++ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
++ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
++ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b. Wall repulsion forces (based on actual radii)
+- for i in range(n):
+- # Left wall
+- overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
++ # b. Wall repulsion forces (vectorized, based on actual radii)
++ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
++ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
++ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
++ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+ def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+- for _ in range(250):
++ # Fewer iterations for performance; high accuracy is less critical during simulation.
++ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae3db02b9c9aa1ea2c02e9d588f23bec25741507
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/main.py
@@ -0,0 +1,288 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration and longer refinement.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fc8c7dbfd5b6e3983eb39b62837085607d66e9d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/original.py
@@ -0,0 +1,309 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 1000 # Increased iterations for thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 200 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (based on actual radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2e57a02bc51eb861b8ad2cf65e5f457bceeaf643
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_67/search_replace.txt
@@ -0,0 +1,126 @@
+The current simulation-based approach yields a score of 1.95, which is significantly lower than the 2.27 achieved by a previous Genetic Algorithm. The visualization reveals that the packing is stuck in a local optimum, closely resembling the initial 5x5 grid structure. This rigidity prevents the circles from rearranging into a denser, more optimal configuration, resulting in substantial wasted space.
+
+My proposed strategy aims to break this grid-lock by making the simulation more exploratory and dynamic, borrowing principles from successful evolutionary and high-energy simulation methods.
+
+1. **Introduce Initial Perturbation**: A small random noise is added to the 25 grid centers at the start of each simulation run. This breaks the perfect symmetry of the grid, providing a more varied landscape for the optimization process and preventing it from immediately converging to the same suboptimal state.
+2. **Increase Simulation Aggressiveness & Duration**: The `initial_growth_pressure` is increased to provide a stronger initial "melt," forcing the circles apart more energetically to explore new arrangements. The total simulation and fine-tuning iterations are doubled, giving this more complex system sufficient time to settle into a higher-quality equilibrium.
+3. **Optimize for Speed**: To make a longer simulation feasible, I am vectorizing the wall repulsion calculations, replacing slow Python loops with highly efficient NumPy operations. This optimization is applied to both the main simulation and the fine-tuning phase. Additionally, I am reducing the iterations within the `compute_radii_for_simulation` function, as high-precision radii are less critical during the dynamic phases of the simulation, providing a significant performance boost.
+
+Together, these changes create a more powerful and efficient simulation capable of escaping the initial grid's local minimum and discovering packings with a significantly higher sum of radii.
+
+
+aggressive_exploratory_simulation
+
+
+This set of changes introduces a more aggressive and exploratory simulation strategy to break out of the initial grid's local optimum. Key improvements include:
+1. **Initial Perturbation**: Adds a small random noise to the initial 25 grid centers to break symmetry.
+2. **Increased Aggressiveness**: Raises `initial_growth_pressure` and doubles `simulation_iterations` and `fine_tune_iter` to promote exploration and allow for better settlement.
+3. **Performance Optimization**: Vectorizes wall repulsion calculations for a significant speedup and reduces iterations in the simulation's radius calculation function to make the longer simulation time feasible.
+This combined approach aims to unlock more complex and efficient packing configurations, moving closer to the performance of genetic algorithms by enhancing the simulation's exploratory power.
+
+
+<<<<<<< SEARCH
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 1000 # Increased iterations for thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 200 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+=======
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration and longer refinement.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+>>>>>>> REPLACE
+<<<<<<< SEARCH
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+=======
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+>>>>>>> REPLACE
+<<<<<<< SEARCH
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+=======
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+>>>>>>> REPLACE
+<<<<<<< SEARCH
+ # b. Wall repulsion forces (based on actual radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+=======
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+>>>>>>> REPLACE
+<<<<<<< SEARCH
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+=======
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ for _ in range(100):
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4fb9a2fbce2b817332f6adb4294e513b77affc60
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..5432d58a3eb7cd9169103844b941e1215789e301
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/edit.diff
@@ -0,0 +1,486 @@
+--- a/original.py
++++ b/original.py
+@@ -1,223 +1,296 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
++# Helper function for calculating radii - derived from the best performing GA version
++# and refined with vectorized operations.
++def compute_max_radii(centers, iterations=250):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ This uses an iterative proportional scaling method.
++ """
++ n = centers.shape[0]
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ # Iteratively adjust radii to resolve overlaps and maximize size
++ for _ in range(iterations):
++ updated = False
++
++ # Vectorized calculations for pairwise distances
++ # Compute differences between all pairs of centers
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ # Compute Euclidean distances
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++
++ # Handle co-located centers: identify pairs where distance is near zero
++ co_located_mask = dists < 1e-9
++ np.fill_diagonal(co_located_mask, False) # A circle is not co-located with itself
++
++ # Calculate potential overlaps using current radii
++ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
++ overlap_mask = radii_sums > dists
++
++ # Identify pairs that currently overlap AND are not co-located
++ active_overlap_mask = overlap_mask & ~co_located_mask
++
++ if np.any(active_overlap_mask):
++ updated = True
++ # Get unique indices of overlapping pairs (from upper triangle to process each pair once)
++ i_indices, j_indices = np.where(np.triu(active_overlap_mask, k=1))
++
++ for i, j in zip(i_indices, j_indices):
++ dist = dists[i, j] # Already computed distance
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++
++ # Explicitly handle co-located centers by setting their radii to zero
++ if np.any(co_located_mask):
++ updated = True
++ i_indices, j_indices = np.where(np.triu(co_located_mask, k=1))
++ for i, j in zip(i_indices, j_indices):
++ # Only update if they actually had positive radii before
++ if radii[i] > 0 or radii[j] > 0:
++ radii[i], radii[j] = 0.0, 0.0
++
++ if not updated:
++ break # Radii have stabilized
++
++ return np.maximum(radii, 0.0) # Ensure no negative radii
++
++class Individual:
++ """Represents a single candidate packing solution (a set of circle centers)."""
++ def __init__(self, centers_array, n_circles):
++ self.centers = np.array(centers_array).reshape(n_circles, 2)
++ self.radii = None
++ self.fitness = -1.0 # Sum of radii, initialized to a low value
++
++ def evaluate_fitness(self, radius_iter_val):
++ """Calculates the sum of radii for the current center configuration."""
++ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
++ self.fitness = np.sum(self.radii)
++ return self.fitness
++
+ class ConfigurableHybridPacker:
+ """
+- A class-based packer encapsulating state and logic for a hybrid
+- 'initialize-and-refine' strategy. This object-oriented structure improves
+- maintainability and separates concerns, with hyperparameters managed in a
+- centralized configuration dictionary.
++ A class-based packer implementing a Genetic Algorithm to find optimal
++ circle packings. It leverages a multi-start initialization, tournament
++ selection, uniform crossover, and annealed Gaussian mutation.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+- self.centers = np.zeros((n, 2))
+- self.radii = np.zeros(n)
+-
+- def _initialize_centers(self):
+- """Sets up the initial circle positions using the best-known static 5x5 grid."""
++ self.population = []
++ self.best_individual_overall = None
++
++ def initialize_population(self):
++ """
++ Initializes a diverse population of individuals for the Genetic Algorithm.
++ Includes a known good starting grid, perturbed versions, and random configurations.
++ Combines elements from multiple prior approaches for robust exploration.
++ """
++ population_size = self.config['POPULATION_SIZE']
++ n_circles = self.n
++ population = []
++
++ # Base 5x5 grid setup for 25 circles
+ num_cells_side = 5
+- spacing = 1.0 / num_cells_side
++ spacing = 1.0 / num_cells_side # 0.2
++ grid_centers_template = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+- self.centers[k, 0] = (i + 0.5) * spacing
+- self.centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+- # Place the 26th circle in the optimal interstitial void found in prior runs.
+- self.centers[25] = [spacing, spacing]
+-
+- def _compute_radii_for_centers(self, centers_to_eval):
+- """
+- Computes maximum radii for a given set of centers using iterative proportional scaling.
+- This is a core utility used both during and after the simulation.
+- """
+- radii = np.min([
+- centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+- centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+- ], axis=0)
+-
+- for _ in range(self.config['radius_iter']):
+- updated = False
+- for i in range(self.n):
+- for j in range(i + 1, self.n):
+- dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+-
+- if radii[i] + radii[j] > dist:
+- if dist < 1e-9: # Handle co-located centers
+- radii[i], radii[j] = 0.0, 0.0
+- else:
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+- updated = True
+- if not updated:
+- break
+- return np.maximum(radii, 0)
+-
+- def _run_refinement_simulation(self):
+- """
+- Performs a force-directed simulation to refine circle positions.
+- This simulation uses a 'growth pressure' concept to actively
+- encourage circles to expand and fill empty space.
+- """
+- iterations = self.config['sim_iter']
+- base_lr = self.config['learning_rate']
+- wall_strength = self.config['wall_strength']
+- initial_growth_pressure = self.config['initial_growth_pressure']
+- final_growth_pressure = self.config['final_growth_pressure']
+-
+- for i_iter in range(iterations):
+- # Anneal growth pressure quadratically from high to low.
+- # This 'melts' the initial configuration and then lets it 'recrystallize'.
+- progress = i_iter / iterations
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+-
+- # 1. Calculate the maximum non-overlapping radii for the current positions.
+- current_radii = self._compute_radii_for_centers(self.centers)
+-
+- # 2. Artificially inflate radii to create an expansion 'pressure'.
+- pressured_radii = current_radii * current_growth_pressure
+-
+- forces = np.zeros_like(self.centers)
+-
+- # 3. Calculate repulsion forces based on the artificial overlaps.
+- # a) Circle-to-circle repulsion (vectorized)
+- # Calculate pairwise differences and distances
+- diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+-
+- # Prevent division by zero for co-located circles
+- dists[dists < 1e-9] = 1e-9
+-
+- # Calculate pairwise radius sums and then overlaps
+- radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+- np.fill_diagonal(overlaps, 0) # No self-repulsion
+-
+- # Calculate force magnitudes and apply them along the normalized difference vectors
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+-
+- # Sum forces for each circle
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b) Wall repulsion (vectorized)
+- # Left wall (x=0)
+- overlap_l = pressured_radii - self.centers[:, 0]
+- forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+- # Right wall (x=1)
+- overlap_r = (self.centers[:, 0] + pressured_radii) - 1.0
+- forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+- # Bottom wall (y=0)
+- overlap_b = pressured_radii - self.centers[:, 1]
+- forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+- # Top wall (y=1)
+- overlap_t = (self.centers[:, 1] + pressured_radii) - 1.0
+- forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+-
+- # 4. Update center positions with a decaying learning rate for stability.
+- lr = base_lr * (1.0 - (i_iter / iterations))**2
+- self.centers += forces * lr
+-
+- # 5. Enforce hard boundary constraints.
+- self.centers = np.clip(self.centers, 0.0, 1.0)
+-
+- def _run_fine_tuning(self):
+- """
+- Performs a final, short simulation with no growth pressure. This allows
+- the packing to settle and resolve any minor residual overlaps from the
+- main simulation, enabling a more precise final state.
+- """
+- iterations = self.config['fine_tune_iter']
+- lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+- wall_strength = self.config['wall_strength']
+-
+- for _ in range(iterations):
+- # 1. Calculate radii for the current positions. NO growth pressure.
+- current_radii = self._compute_radii_for_centers(self.centers)
+-
+- forces = np.zeros_like(self.centers)
+-
+- # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+- # a) Circle-to-circle repulsion (vectorized)
+- diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9
+-
+- # Overlap is based on actual radii.
+- radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0
+- np.fill_diagonal(overlaps, 0)
+-
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b) Wall repulsion (vectorized, based on actual radii)
+- # Left wall (x=0)
+- overlap_l = current_radii - self.centers[:, 0]
+- forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+- # Right wall (x=1)
+- overlap_r = (self.centers[:, 0] + current_radii) - 1.0
+- forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+- # Bottom wall (y=0)
+- overlap_b = current_radii - self.centers[:, 1]
+- forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+- # Top wall (y=1)
+- overlap_t = (self.centers[:, 1] + current_radii) - 1.0
+- forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+-
+- # 3. Update positions with small learning rate.
+- self.centers += forces * lr
+-
+- # 4. Enforce hard boundary constraints.
+- self.centers = np.clip(self.centers, 0.0, 1.0)
++ if k < n_circles -1 : # Fill 25 circles
++ grid_centers_template[k, 0] = (i + 0.5) * spacing
++ grid_centers_template[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ # 1. Add baseline 5x5 grid + specific interstitial (0.2, 0.2)
++ grid_centers_base = grid_centers_template.copy()
++ grid_centers_base[n_circles - 1] = [spacing, spacing]
++ population.append(Individual(grid_centers_base, n_circles))
++
++ # 2. Add perturbed versions of this strong base configuration
++ for _ in range(population_size // 10):
++ perturbed_grid_centers = grid_centers_base + np.random.normal(0, self.config['INITIAL_MUTATION_STRENGTH'] / 2, (n_circles, 2))
++ perturbed_grid_centers = np.clip(perturbed_grid_centers, 0.0, 1.0)
++ population.append(Individual(perturbed_grid_centers, n_circles))
++
++ # 3. Add more diverse initial positions by considering other strategic extra circle positions
++ # from multi-start simulation programs, ensuring population size is not exceeded.
++ candidate_extra_positions = [
++ [spacing, 0.5], # (0.2, 0.5)
++ [spacing, 1 - spacing], # (0.2, 0.8)
++ [0.5, spacing], # (0.5, 0.2)
++ [0.5, 0.5], # (0.5, 0.5) - Center
++ [0.5, 1 - spacing], # (0.5, 0.8)
++ [1 - spacing, spacing], # (0.8, 0.2)
++ [1 - spacing, 0.5], # (0.8, 0.5)
++ [1 - spacing, 1 - spacing], # (0.8, 0.8)
++ [0.05, 0.05], # Corner offsets
++ [0.05, 0.95],
++ [0.95, 0.05],
++ [0.95, 0.95]
++ ]
++
++ for extra_pos in candidate_extra_positions:
++ if len(population) >= population_size: break
++ new_centers = grid_centers_template.copy()
++ new_centers[n_circles-1] = np.array(extra_pos)
++ population.append(Individual(new_centers, n_circles))
++
++ # 4. Fill the rest of the population with random initializations for broad diversity
++ while len(population) < population_size:
++ random_centers = np.random.rand(n_circles, 2)
++ population.append(Individual(random_centers, n_circles))
++
++ # Evaluate fitness of the initial population
++ for individual in population:
++ individual.evaluate_fitness(self.config['GA_RADIUS_ITER'])
++
++ self.population = population
++ self.best_individual_overall = max(population, key=lambda ind: ind.fitness)
++
++ def select_parents(self):
++ """
++ Selects two parents using tournament selection.
++ """
++ tournament_size = self.config['TOURNAMENT_SIZE']
++
++ # Select parent 1
++ tournament_candidates_p1 = np.random.choice(self.population, tournament_size, replace=False)
++ parent1 = max(tournament_candidates_p1, key=lambda ind: ind.fitness)
++
++ # Select parent 2, ensuring it's different from parent 1 if possible
++ # Create a pool of candidates for parent 2 that excludes parent 1
++ candidates_for_p2_pool = [ind for ind in self.population if ind is not parent1]
++
++ if len(candidates_for_p2_pool) >= tournament_size:
++ # If there are enough distinct candidates, run a full tournament
++ tournament_candidates_p2 = np.random.choice(candidates_for_p2_pool, tournament_size, replace=False)
++ parent2 = max(tournament_candidates_p2, key=lambda ind: ind.fitness)
++ elif len(candidates_for_p2_pool) > 0:
++ # If not enough for a full tournament, pick the fittest from available distinct candidates
++ parent2 = max(candidates_for_p2_pool, key=lambda ind: ind.fitness)
++ else:
++ # Fallback: if parent1 is the only individual (e.g., population_size is very small)
++ # or all others are identical by object reference, pick any other.
++ # This case is rare with typical population sizes.
++ parent2 = np.random.choice(self.population)
++
++ return parent1, parent2
++
++ def crossover(self, parent1, parent2):
++ """
++ Performs uniform crossover: for each circle, its position (x,y) is inherited
++ from either parent1 or parent2 with 50% probability.
++ """
++ n_circles = self.n
++ child_centers = np.zeros((n_circles, 2))
++ for i in range(n_circles):
++ if np.random.rand() < 0.5:
++ child_centers[i] = parent1.centers[i]
++ else:
++ child_centers[i] = parent2.centers[i]
++
++ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles)
++
++ def mutate(self, individual, mutation_rate, mutation_strength):
++ """
++ Mutates an individual's circle center positions.
++ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
++ perturbed by Gaussian noise with `mutation_strength` standard deviation.
++ """
++ n_circles = self.n
++ mutated_centers = individual.centers.copy()
++ for i in range(n_circles):
++ if np.random.rand() < mutation_rate:
++ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
++ mutated_centers[i] = np.clip(mutated_centers[i], 0.0, 1.0)
++ return Individual(mutated_centers, n_circles)
+
+ def pack(self):
+- """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+- self._initialize_centers()
+- self._run_refinement_simulation()
+- self._run_fine_tuning()
+- # Final computation of radii for the refined centers.
+- self.radii = self._compute_radii_for_centers(self.centers)
+- return self.centers, self.radii
++ """
++ Executes the Genetic Algorithm to find the best packing.
++ """
++ population_size = self.config['POPULATION_SIZE']
++ num_generations = self.config['NUM_GENERATIONS']
++ mutation_rate = self.config['MUTATION_RATE']
++ initial_mutation_strength = self.config['INITIAL_MUTATION_STRENGTH']
++ final_mutation_strength = self.config['FINAL_MUTATION_STRENGTH']
++ elitism_count = self.config['ELITISM_COUNT']
++ ga_radius_iter = self.config['GA_RADIUS_ITER']
++ final_radius_iter = self.config['FINAL_RADIUS_ITER']
++
++ self.initialize_population()
++
++ for generation in range(num_generations):
++ # Anneal mutation strength
++ progress = generation / num_generations
++ current_mutation_strength = final_mutation_strength + \
++ (initial_mutation_strength - final_mutation_strength) * (1.0 - progress)**2
++
++ new_population = []
++
++ # Elitism: Carry over the best individuals directly
++ sorted_population = sorted(self.population, key=lambda ind: ind.fitness, reverse=True)
++ new_population.extend(sorted_population[:elitism_count])
++
++ # Generate the rest of the new population through selection, crossover, and mutation
++ while len(new_population) < population_size:
++ parent1, parent2 = self.select_parents()
++ child = self.crossover(parent1, parent2)
++ child = self.mutate(child, mutation_rate, current_mutation_strength)
++ child.evaluate_fitness(ga_radius_iter) # Evaluate child with reduced iterations for speed
++ new_population.append(child)
++
++ self.population = new_population
++
++ current_best_in_gen = max(self.population, key=lambda ind: ind.fitness)
++ if current_best_in_gen.fitness > self.best_individual_overall.fitness:
++ self.best_individual_overall = current_best_in_gen
++
++ # Final evaluation of the best individual with high `radius_iter` for maximum precision
++ self.best_individual_overall.evaluate_fitness(final_radius_iter)
++
++ return self.best_individual_overall.centers, self.best_individual_overall.radii
++
+
+ def construct_packing():
+ """
+- Main function to construct the circle packing. This implementation shifts from
+- a procedural script to an object-oriented approach by using the
+- ConfigurableHybridPacker class to manage the packing process.
+- """
+- # Hyperparameters are now centralized in a configuration dictionary.
+- # We introduce an annealing schedule for growth pressure to break out of
+- # the initial grid's local minimum and a final fine-tuning phase.
++ Main function to construct the circle packing using the ConfigurableHybridPacker.
++ This uses a Genetic Algorithm to maximize the sum of radii.
++ """
++ # Genetic Algorithm Hyperparameters are centralized in a configuration dictionary.
+ packer_config = {
+- 'sim_iter': 2000, # Doubled iterations for more thorough refinement.
+- 'radius_iter': 250, # Sufficient iterations for radius convergence.
+- 'learning_rate': 0.02, # A balanced learning rate for exploration.
+- 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+- 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+- 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+- 'fine_tune_iter': 400, # Doubled iterations for final settlement.
+- 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
++ 'POPULATION_SIZE': 150, # Number of candidate solutions in each generation
++ 'NUM_GENERATIONS': 2500, # Total number of evolutionary steps
++ 'MUTATION_RATE': 0.15, # Probability that a single circle's position is mutated
++ 'INITIAL_MUTATION_STRENGTH': 0.08, # Initial standard deviation of Gaussian noise for mutation
++ 'FINAL_MUTATION_STRENGTH': 0.001, # Final standard deviation (annealed)
++ 'TOURNAMENT_SIZE': 7, # Number of individuals to compete in tournament selection
++ 'ELITISM_COUNT': 10, # Number of best individuals to directly carry over to next generation
++
++ # Radius calculation iterations: lower for speed during GA, higher for final precision
++ 'GA_RADIUS_ITER': 60, # Iterations for `compute_max_radii` during GA evolution
++ 'FINAL_RADIUS_ITER': 350, # Iterations for final best solution evaluation
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..346d48c75c8ce9243f6856304340888a276eda66
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/main.py
@@ -0,0 +1,296 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# Helper function for calculating radii - derived from the best performing GA version
+# and refined with vectorized operations.
+def compute_max_radii(centers, iterations=250):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively adjust radii to resolve overlaps and maximize size
+ for _ in range(iterations):
+ updated = False
+
+ # Vectorized calculations for pairwise distances
+ # Compute differences between all pairs of centers
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ # Compute Euclidean distances
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Handle co-located centers: identify pairs where distance is near zero
+ co_located_mask = dists < 1e-9
+ np.fill_diagonal(co_located_mask, False) # A circle is not co-located with itself
+
+ # Calculate potential overlaps using current radii
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlap_mask = radii_sums > dists
+
+ # Identify pairs that currently overlap AND are not co-located
+ active_overlap_mask = overlap_mask & ~co_located_mask
+
+ if np.any(active_overlap_mask):
+ updated = True
+ # Get unique indices of overlapping pairs (from upper triangle to process each pair once)
+ i_indices, j_indices = np.where(np.triu(active_overlap_mask, k=1))
+
+ for i, j in zip(i_indices, j_indices):
+ dist = dists[i, j] # Already computed distance
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Explicitly handle co-located centers by setting their radii to zero
+ if np.any(co_located_mask):
+ updated = True
+ i_indices, j_indices = np.where(np.triu(co_located_mask, k=1))
+ for i, j in zip(i_indices, j_indices):
+ # Only update if they actually had positive radii before
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+
+ if not updated:
+ break # Radii have stabilized
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer implementing a Genetic Algorithm to find optimal
+ circle packings. It leverages a multi-start initialization, tournament
+ selection, uniform crossover, and annealed Gaussian mutation.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.population = []
+ self.best_individual_overall = None
+
+ def initialize_population(self):
+ """
+ Initializes a diverse population of individuals for the Genetic Algorithm.
+ Includes a known good starting grid, perturbed versions, and random configurations.
+ Combines elements from multiple prior approaches for robust exploration.
+ """
+ population_size = self.config['POPULATION_SIZE']
+ n_circles = self.n
+ population = []
+
+ # Base 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # 0.2
+ grid_centers_template = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles -1 : # Fill 25 circles
+ grid_centers_template[k, 0] = (i + 0.5) * spacing
+ grid_centers_template[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # 1. Add baseline 5x5 grid + specific interstitial (0.2, 0.2)
+ grid_centers_base = grid_centers_template.copy()
+ grid_centers_base[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers_base, n_circles))
+
+ # 2. Add perturbed versions of this strong base configuration
+ for _ in range(population_size // 10):
+ perturbed_grid_centers = grid_centers_base + np.random.normal(0, self.config['INITIAL_MUTATION_STRENGTH'] / 2, (n_circles, 2))
+ perturbed_grid_centers = np.clip(perturbed_grid_centers, 0.0, 1.0)
+ population.append(Individual(perturbed_grid_centers, n_circles))
+
+ # 3. Add more diverse initial positions by considering other strategic extra circle positions
+ # from multi-start simulation programs, ensuring population size is not exceeded.
+ candidate_extra_positions = [
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ for extra_pos in candidate_extra_positions:
+ if len(population) >= population_size: break
+ new_centers = grid_centers_template.copy()
+ new_centers[n_circles-1] = np.array(extra_pos)
+ population.append(Individual(new_centers, n_circles))
+
+ # 4. Fill the rest of the population with random initializations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ # Evaluate fitness of the initial population
+ for individual in population:
+ individual.evaluate_fitness(self.config['GA_RADIUS_ITER'])
+
+ self.population = population
+ self.best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ def select_parents(self):
+ """
+ Selects two parents using tournament selection.
+ """
+ tournament_size = self.config['TOURNAMENT_SIZE']
+
+ # Select parent 1
+ tournament_candidates_p1 = np.random.choice(self.population, tournament_size, replace=False)
+ parent1 = max(tournament_candidates_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1 if possible
+ # Create a pool of candidates for parent 2 that excludes parent 1
+ candidates_for_p2_pool = [ind for ind in self.population if ind is not parent1]
+
+ if len(candidates_for_p2_pool) >= tournament_size:
+ # If there are enough distinct candidates, run a full tournament
+ tournament_candidates_p2 = np.random.choice(candidates_for_p2_pool, tournament_size, replace=False)
+ parent2 = max(tournament_candidates_p2, key=lambda ind: ind.fitness)
+ elif len(candidates_for_p2_pool) > 0:
+ # If not enough for a full tournament, pick the fittest from available distinct candidates
+ parent2 = max(candidates_for_p2_pool, key=lambda ind: ind.fitness)
+ else:
+ # Fallback: if parent1 is the only individual (e.g., population_size is very small)
+ # or all others are identical by object reference, pick any other.
+ # This case is rare with typical population sizes.
+ parent2 = np.random.choice(self.population)
+
+ return parent1, parent2
+
+ def crossover(self, parent1, parent2):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability.
+ """
+ n_circles = self.n
+ child_centers = np.zeros((n_circles, 2))
+ for i in range(n_circles):
+ if np.random.rand() < 0.5:
+ child_centers[i] = parent1.centers[i]
+ else:
+ child_centers[i] = parent2.centers[i]
+
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles)
+
+ def mutate(self, individual, mutation_rate, mutation_strength):
+ """
+ Mutates an individual's circle center positions.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ n_circles = self.n
+ mutated_centers = individual.centers.copy()
+ for i in range(n_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+ mutated_centers[i] = np.clip(mutated_centers[i], 0.0, 1.0)
+ return Individual(mutated_centers, n_circles)
+
+ def pack(self):
+ """
+ Executes the Genetic Algorithm to find the best packing.
+ """
+ population_size = self.config['POPULATION_SIZE']
+ num_generations = self.config['NUM_GENERATIONS']
+ mutation_rate = self.config['MUTATION_RATE']
+ initial_mutation_strength = self.config['INITIAL_MUTATION_STRENGTH']
+ final_mutation_strength = self.config['FINAL_MUTATION_STRENGTH']
+ elitism_count = self.config['ELITISM_COUNT']
+ ga_radius_iter = self.config['GA_RADIUS_ITER']
+ final_radius_iter = self.config['FINAL_RADIUS_ITER']
+
+ self.initialize_population()
+
+ for generation in range(num_generations):
+ # Anneal mutation strength
+ progress = generation / num_generations
+ current_mutation_strength = final_mutation_strength + \
+ (initial_mutation_strength - final_mutation_strength) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Carry over the best individuals directly
+ sorted_population = sorted(self.population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:elitism_count])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < population_size:
+ parent1, parent2 = self.select_parents()
+ child = self.crossover(parent1, parent2)
+ child = self.mutate(child, mutation_rate, current_mutation_strength)
+ child.evaluate_fitness(ga_radius_iter) # Evaluate child with reduced iterations for speed
+ new_population.append(child)
+
+ self.population = new_population
+
+ current_best_in_gen = max(self.population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > self.best_individual_overall.fitness:
+ self.best_individual_overall = current_best_in_gen
+
+ # Final evaluation of the best individual with high `radius_iter` for maximum precision
+ self.best_individual_overall.evaluate_fitness(final_radius_iter)
+
+ return self.best_individual_overall.centers, self.best_individual_overall.radii
+
+
+def construct_packing():
+ """
+ Main function to construct the circle packing using the ConfigurableHybridPacker.
+ This uses a Genetic Algorithm to maximize the sum of radii.
+ """
+ # Genetic Algorithm Hyperparameters are centralized in a configuration dictionary.
+ packer_config = {
+ 'POPULATION_SIZE': 150, # Number of candidate solutions in each generation
+ 'NUM_GENERATIONS': 2500, # Total number of evolutionary steps
+ 'MUTATION_RATE': 0.15, # Probability that a single circle's position is mutated
+ 'INITIAL_MUTATION_STRENGTH': 0.08, # Initial standard deviation of Gaussian noise for mutation
+ 'FINAL_MUTATION_STRENGTH': 0.001, # Final standard deviation (annealed)
+ 'TOURNAMENT_SIZE': 7, # Number of individuals to compete in tournament selection
+ 'ELITISM_COUNT': 10, # Number of best individuals to directly carry over to next generation
+
+ # Radius calculation iterations: lower for speed during GA, higher for final precision
+ 'GA_RADIUS_ITER': 60, # Iterations for `compute_max_radii` during GA evolution
+ 'FINAL_RADIUS_ITER': 350, # Iterations for final best solution evaluation
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..4d61b633806ab6c86af21ff700f268c84f806ce7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/original.py
@@ -0,0 +1,223 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _initialize_centers(self):
+ """Sets up the initial circle positions using the best-known static 5x5 grid."""
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ self.centers[k, 0] = (i + 0.5) * spacing
+ self.centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in the optimal interstitial void found in prior runs.
+ self.centers[25] = [spacing, spacing]
+
+ def _compute_radii_for_centers(self, centers_to_eval):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(self.config['radius_iter']):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized)
+ # Left wall (x=0)
+ overlap_l = pressured_radii - self.centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (self.centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - self.centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (self.centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers)
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (vectorized, based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - self.centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (self.centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - self.centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (self.centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self._initialize_centers()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers)
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation shifts from
+ a procedural script to an object-oriented approach by using the
+ ConfigurableHybridPacker class to manage the packing process.
+ """
+ # Hyperparameters are now centralized in a configuration dictionary.
+ # We introduce an annealing schedule for growth pressure to break out of
+ # the initial grid's local minimum and a final fine-tuning phase.
+ packer_config = {
+ 'sim_iter': 2000, # Doubled iterations for more thorough refinement.
+ 'radius_iter': 250, # Sufficient iterations for radius convergence.
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.02, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 400, # Doubled iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..8d40430740350e5459e0108a5076e0a9490435ee
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": false,
+ "error": "Validation failed: Circle 2 (x=0.3292, y=0.0517, r=0.1711) is outside unit square."
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..f457ee9ad18fce37bbd83931e15f40c902341317
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results
+Run 1/1 completed in 1907.25 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/metrics.json
+Evaluation or Validation failed: Validation failed: Circle 2 (x=0.3292, y=0.0517, r=0.1711) is outside unit square.
+Metrics:
+ combined_score: 2.633831919951497
+ public: {'centers_str': ' centers[0] = (0.0652, 0.0650)\n centers[1] = (0.5219, 0.1982)\n centers[2] = (0.3292, 0.0517)\n centers[3] = (0.6925, 0.0206)\n centers[4] = (0.9562, 0.0493)\n centers[5] = (0.0386, 0.2231)\n centers[6] = (0.4338, 0.3214)\n centers[7] = (0.2597, 0.3148)\n centers[8] = (0.5816, 0.3598)\n centers[9] = (0.9595, 0.2838)\n centers[10] = (0.0519, 0.4859)\n centers[11] = (0.3083, 0.5018)\n centers[12] = (0.4818, 0.4761)\n centers[13] = (0.7339, 0.4615)\n centers[14] = (0.9100, 0.5091)\n centers[15] = (0.1021, 0.7048)\n centers[16] = (0.2676, 0.6941)\n centers[17] = (0.4996, 0.6894)\n centers[18] = (0.7295, 0.6852)\n centers[19] = (0.9575, 0.6807)\n centers[20] = (0.0693, 0.9160)\n centers[21] = (0.3500, 0.9535)\n centers[22] = (0.5754, 0.9322)\n centers[23] = (0.6989, 0.8427)\n centers[24] = (0.8828, 0.9373)\n centers[25] = (0.5022, 0.1963)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.633831919951497}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/packing_viz.png
+ execution_time_mean: 1907.247290416155
+ execution_time_std: 0.0
+ num_valid_runs: 0
+ num_invalid_runs: 1
+ all_validation_errors: ['Circle 2 (x=0.3292, y=0.0517, r=0.1711) is outside unit square.']
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..a23c34e749f297bf962199653a1e1743a8dd30e9
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/metrics.json
@@ -0,0 +1,18 @@
+{
+ "combined_score": 2.633831919951497,
+ "public": {
+ "centers_str": " centers[0] = (0.0652, 0.0650)\n centers[1] = (0.5219, 0.1982)\n centers[2] = (0.3292, 0.0517)\n centers[3] = (0.6925, 0.0206)\n centers[4] = (0.9562, 0.0493)\n centers[5] = (0.0386, 0.2231)\n centers[6] = (0.4338, 0.3214)\n centers[7] = (0.2597, 0.3148)\n centers[8] = (0.5816, 0.3598)\n centers[9] = (0.9595, 0.2838)\n centers[10] = (0.0519, 0.4859)\n centers[11] = (0.3083, 0.5018)\n centers[12] = (0.4818, 0.4761)\n centers[13] = (0.7339, 0.4615)\n centers[14] = (0.9100, 0.5091)\n centers[15] = (0.1021, 0.7048)\n centers[16] = (0.2676, 0.6941)\n centers[17] = (0.4996, 0.6894)\n centers[18] = (0.7295, 0.6852)\n centers[19] = (0.9575, 0.6807)\n centers[20] = (0.0693, 0.9160)\n centers[21] = (0.3500, 0.9535)\n centers[22] = (0.5754, 0.9322)\n centers[23] = (0.6989, 0.8427)\n centers[24] = (0.8828, 0.9373)\n centers[25] = (0.5022, 0.1963)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.633831919951497
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/results/packing_viz.png",
+ "execution_time_mean": 1907.247290416155,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 0,
+ "num_invalid_runs": 1,
+ "all_validation_errors": [
+ "Circle 2 (x=0.3292, y=0.0517, r=0.1711) is outside unit square."
+ ]
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a830fa6ace4786025065d6139436504856d5ead0
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_68/rewrite.txt
@@ -0,0 +1,286 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# Helper function for calculating radii - derived from the best performing GA version
+# and refined with vectorized operations.
+def compute_max_radii(centers, iterations=250):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively adjust radii to resolve overlaps and maximize size
+ for _ in range(iterations):
+ updated = False
+
+ # Vectorized calculations for pairwise distances
+ # Compute differences between all pairs of centers
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ # Compute Euclidean distances
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Handle co-located centers: identify pairs where distance is near zero
+ co_located_mask = dists < 1e-9
+ np.fill_diagonal(co_located_mask, False) # A circle is not co-located with itself
+
+ # Calculate potential overlaps using current radii
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlap_mask = radii_sums > dists
+
+ # Identify pairs that currently overlap AND are not co-located
+ active_overlap_mask = overlap_mask & ~co_located_mask
+
+ if np.any(active_overlap_mask):
+ updated = True
+ # Get unique indices of overlapping pairs (from upper triangle to process each pair once)
+ i_indices, j_indices = np.where(np.triu(active_overlap_mask, k=1))
+
+ for i, j in zip(i_indices, j_indices):
+ dist = dists[i, j] # Already computed distance
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Explicitly handle co-located centers by setting their radii to zero
+ if np.any(co_located_mask):
+ updated = True
+ i_indices, j_indices = np.where(np.triu(co_located_mask, k=1))
+ for i, j in zip(i_indices, j_indices):
+ # Only update if they actually had positive radii before
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+
+ if not updated:
+ break # Radii have stabilized
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer implementing a Genetic Algorithm to find optimal
+ circle packings. It leverages a multi-start initialization, tournament
+ selection, uniform crossover, and annealed Gaussian mutation.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.population = []
+ self.best_individual_overall = None
+
+ def initialize_population(self):
+ """
+ Initializes a diverse population of individuals for the Genetic Algorithm.
+ Includes a known good starting grid, perturbed versions, and random configurations.
+ Combines elements from multiple prior approaches for robust exploration.
+ """
+ population_size = self.config['POPULATION_SIZE']
+ n_circles = self.n
+ population = []
+
+ # Base 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # 0.2
+ grid_centers_template = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles -1 : # Fill 25 circles
+ grid_centers_template[k, 0] = (i + 0.5) * spacing
+ grid_centers_template[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # 1. Add baseline 5x5 grid + specific interstitial (0.2, 0.2)
+ grid_centers_base = grid_centers_template.copy()
+ grid_centers_base[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers_base, n_circles))
+
+ # 2. Add perturbed versions of this strong base configuration
+ for _ in range(population_size // 10):
+ perturbed_grid_centers = grid_centers_base + np.random.normal(0, self.config['INITIAL_MUTATION_STRENGTH'] / 2, (n_circles, 2))
+ perturbed_grid_centers = np.clip(perturbed_grid_centers, 0.0, 1.0)
+ population.append(Individual(perturbed_grid_centers, n_circles))
+
+ # 3. Add more diverse initial positions by considering other strategic extra circle positions
+ # from multi-start simulation programs, ensuring population size is not exceeded.
+ candidate_extra_positions = [
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ for extra_pos in candidate_extra_positions:
+ if len(population) >= population_size: break
+ new_centers = grid_centers_template.copy()
+ new_centers[n_circles-1] = np.array(extra_pos)
+ population.append(Individual(new_centers, n_circles))
+
+ # 4. Fill the rest of the population with random initializations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ # Evaluate fitness of the initial population
+ for individual in population:
+ individual.evaluate_fitness(self.config['GA_RADIUS_ITER'])
+
+ self.population = population
+ self.best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ def select_parents(self):
+ """
+ Selects two parents using tournament selection.
+ """
+ tournament_size = self.config['TOURNAMENT_SIZE']
+
+ # Select parent 1
+ tournament_candidates_p1 = np.random.choice(self.population, tournament_size, replace=False)
+ parent1 = max(tournament_candidates_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1 if possible
+ # Create a pool of candidates for parent 2 that excludes parent 1
+ candidates_for_p2_pool = [ind for ind in self.population if ind is not parent1]
+
+ if len(candidates_for_p2_pool) >= tournament_size:
+ # If there are enough distinct candidates, run a full tournament
+ tournament_candidates_p2 = np.random.choice(candidates_for_p2_pool, tournament_size, replace=False)
+ parent2 = max(tournament_candidates_p2, key=lambda ind: ind.fitness)
+ elif len(candidates_for_p2_pool) > 0:
+ # If not enough for a full tournament, pick the fittest from available distinct candidates
+ parent2 = max(candidates_for_p2_pool, key=lambda ind: ind.fitness)
+ else:
+ # Fallback: if parent1 is the only individual (e.g., population_size is very small)
+ # or all others are identical by object reference, pick any other.
+ # This case is rare with typical population sizes.
+ parent2 = np.random.choice(self.population)
+
+ return parent1, parent2
+
+ def crossover(self, parent1, parent2):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability.
+ """
+ n_circles = self.n
+ child_centers = np.zeros((n_circles, 2))
+ for i in range(n_circles):
+ if np.random.rand() < 0.5:
+ child_centers[i] = parent1.centers[i]
+ else:
+ child_centers[i] = parent2.centers[i]
+
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles)
+
+ def mutate(self, individual, mutation_rate, mutation_strength):
+ """
+ Mutates an individual's circle center positions.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ n_circles = self.n
+ mutated_centers = individual.centers.copy()
+ for i in range(n_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+ mutated_centers[i] = np.clip(mutated_centers[i], 0.0, 1.0)
+ return Individual(mutated_centers, n_circles)
+
+ def pack(self):
+ """
+ Executes the Genetic Algorithm to find the best packing.
+ """
+ population_size = self.config['POPULATION_SIZE']
+ num_generations = self.config['NUM_GENERATIONS']
+ mutation_rate = self.config['MUTATION_RATE']
+ initial_mutation_strength = self.config['INITIAL_MUTATION_STRENGTH']
+ final_mutation_strength = self.config['FINAL_MUTATION_STRENGTH']
+ elitism_count = self.config['ELITISM_COUNT']
+ ga_radius_iter = self.config['GA_RADIUS_ITER']
+ final_radius_iter = self.config['FINAL_RADIUS_ITER']
+
+ self.initialize_population()
+
+ for generation in range(num_generations):
+ # Anneal mutation strength
+ progress = generation / num_generations
+ current_mutation_strength = final_mutation_strength + \
+ (initial_mutation_strength - final_mutation_strength) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Carry over the best individuals directly
+ sorted_population = sorted(self.population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:elitism_count])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < population_size:
+ parent1, parent2 = self.select_parents()
+ child = self.crossover(parent1, parent2)
+ child = self.mutate(child, mutation_rate, current_mutation_strength)
+ child.evaluate_fitness(ga_radius_iter) # Evaluate child with reduced iterations for speed
+ new_population.append(child)
+
+ self.population = new_population
+
+ current_best_in_gen = max(self.population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > self.best_individual_overall.fitness:
+ self.best_individual_overall = current_best_in_gen
+
+ # Final evaluation of the best individual with high `radius_iter` for maximum precision
+ self.best_individual_overall.evaluate_fitness(final_radius_iter)
+
+ return self.best_individual_overall.centers, self.best_individual_overall.radii
+
+
+def construct_packing():
+ """
+ Main function to construct the circle packing using the ConfigurableHybridPacker.
+ This uses a Genetic Algorithm to maximize the sum of radii.
+ """
+ # Genetic Algorithm Hyperparameters are centralized in a configuration dictionary.
+ packer_config = {
+ 'POPULATION_SIZE': 150, # Number of candidate solutions in each generation
+ 'NUM_GENERATIONS': 2500, # Total number of evolutionary steps
+ 'MUTATION_RATE': 0.15, # Probability that a single circle's position is mutated
+ 'INITIAL_MUTATION_STRENGTH': 0.08, # Initial standard deviation of Gaussian noise for mutation
+ 'FINAL_MUTATION_STRENGTH': 0.001, # Final standard deviation (annealed)
+ 'TOURNAMENT_SIZE': 7, # Number of individuals to compete in tournament selection
+ 'ELITISM_COUNT': 10, # Number of best individuals to directly carry over to next generation
+
+ # Radius calculation iterations: lower for speed during GA, higher for final precision
+ 'GA_RADIUS_ITER': 60, # Iterations for `compute_max_radii` during GA evolution
+ 'FINAL_RADIUS_ITER': 350, # Iterations for final best solution evaluation
+ }
+
+ packer = ConfigurableHybridPacker(n=26, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..272087b359327d68d55850229a0c7a1f17904672
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..467b92e61d6f7177a3c6e7c682424574326e3f6d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/edit.diff
@@ -0,0 +1,373 @@
+--- a/original.py
++++ b/original.py
+@@ -1,304 +1,299 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+- main_iterations = 1200 # Number of steps for main force-directed simulation
+- main_learning_rate = 0.01 # Initial learning rate for main phase
+- wall_repulsion_strength = 0.35 # Balanced wall repulsion to utilize boundaries effectively
+- initial_growth_pressure = 1.018 # Stronger initial growth pressure for aggressive exploration
++ main_iterations = 2500 # Increased iterations for more thorough exploration after vectorization
++ main_learning_rate = 0.015 # Slightly increased initial learning rate for faster initial movements
++ wall_repulsion_strength = 0.4 # Slightly stronger wall repulsion to better contain circles and utilize boundaries
++ initial_growth_pressure = 1.03 # Increased initial growth pressure for more aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+- fine_tune_iterations = 300 # Number of steps for fine-tuning
+- fine_tune_learning_rate = 0.0008 # Very low learning rate for precise adjustments
+- finetune_growth_pressure_start = 1.0005 # Subtle growth pressure for fine-tuning
++ fine_tune_iterations = 1000 # Increased fine-tune iterations for more precise settling
++ fine_tune_learning_rate = 0.001 # Slightly increased learning rate for fine-tuning to allow more movement
++ finetune_growth_pressure_start = 1.0008 # Subtle growth pressure for fine-tuning, slightly stronger nudge
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+- # a. Circle-to-circle repulsion forces (based on pressured radii)
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers_for_sim[i] - centers_for_sim[j]
+- dist = np.linalg.norm(vec)
+-
+- if dist < 1e-9: # Centers are too close.
+- continue
+-
+- overlap = pressured_radii[i] + pressured_radii[j] - dist
+-
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec # Equal and opposite reaction
+-
+- # b. Wall repulsion forces (based on pressured radii)
+- for i in range(n):
+- overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
++ # a. Vectorized Circle-to-circle repulsion forces
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++
++ overlaps = np.maximum(0, sum_pressured_radii - dists)
++ np.fill_diagonal(overlaps, 0) # No self-repulsion
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ # Avoid division by zero for identical centers, resulting in zero force
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
++
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ # b. Vectorized Wall repulsion forces
++ wall_forces = np.zeros_like(centers_for_sim)
++ overlap_left = pressured_radii - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
++ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
++
++ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
++ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
++ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
++ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
++
++ forces += wall_forces # Add wall forces to total forces
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+- # a. Circle-to-circle repulsion forces
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers_for_sim[i] - centers_for_sim[j]
+- dist = np.linalg.norm(vec)
+-
+- if dist < 1e-9:
+- continue
+-
+- overlap = pressured_radii_ft[i] + pressured_radii_ft[j] - dist
+-
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces_ft[i] += force_vec
+- forces_ft[j] -= force_vec
+-
+- # b. Wall repulsion forces
+- for i in range(n):
+- overlap_left = pressured_radii_ft[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces_ft[i, 0] += wall_repulsion_strength * overlap_left
+- overlap_right = (centers_for_sim[i, 0] + pressured_radii_ft[i]) - 1.0
+- if overlap_right > 0:
+- forces_ft[i, 0] -= wall_repulsion_strength * overlap_right
+- overlap_bottom = pressured_radii_ft[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces_ft[i, 1] += wall_repulsion_strength * overlap_bottom
+- overlap_top = (centers_for_sim[i, 1] + pressured_radii_ft[i]) - 1.0
+- if overlap_top > 0:
+- forces_ft[i, 1] -= wall_repulsion_strength * overlap_top
++ # a. Vectorized Circle-to-circle repulsion forces
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
++
++ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
++
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ forces_ft += np.sum(force_matrix, axis=1)
++
++ # b. Vectorized Wall repulsion forces
++ wall_forces_ft = np.zeros_like(centers_for_sim)
++ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
++ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
++ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
++ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
++
++ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
++ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
++ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
++ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
++
++ forces_ft += wall_forces_ft
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+ def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+- for _ in range(250):
++ # Increased iterations for more accurate radii during simulation, leveraging faster force computations.
++ for _ in range(400):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5a728fc1478ba88807ac0b74fec60f793dbc2b1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/main.py
@@ -0,0 +1,299 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+ main_iterations = 2500 # Increased iterations for more thorough exploration after vectorization
+ main_learning_rate = 0.015 # Slightly increased initial learning rate for faster initial movements
+ wall_repulsion_strength = 0.4 # Slightly stronger wall repulsion to better contain circles and utilize boundaries
+ initial_growth_pressure = 1.03 # Increased initial growth pressure for more aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+ fine_tune_iterations = 1000 # Increased fine-tune iterations for more precise settling
+ fine_tune_learning_rate = 0.001 # Slightly increased learning rate for fine-tuning to allow more movement
+ finetune_growth_pressure_start = 1.0008 # Subtle growth pressure for fine-tuning, slightly stronger nudge
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ # Avoid division by zero for identical centers, resulting in zero force
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces += wall_forces # Add wall forces to total forces
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces_ft += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces_ft = np.zeros_like(centers_for_sim)
+ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+
+ forces_ft += wall_forces_ft
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, leveraging faster force computations.
+ for _ in range(400):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..392c1830f2d91ecbf79feef1962c630f2f64903c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/original.py
@@ -0,0 +1,304 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+ main_iterations = 1200 # Number of steps for main force-directed simulation
+ main_learning_rate = 0.01 # Initial learning rate for main phase
+ wall_repulsion_strength = 0.35 # Balanced wall repulsion to utilize boundaries effectively
+ initial_growth_pressure = 1.018 # Stronger initial growth pressure for aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+ fine_tune_iterations = 300 # Number of steps for fine-tuning
+ fine_tune_learning_rate = 0.0008 # Very low learning rate for precise adjustments
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure for fine-tuning
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ overlap = pressured_radii_ft[i] + pressured_radii_ft[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces_ft[i] += force_vec
+ forces_ft[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ overlap_left = pressured_radii_ft[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces_ft[i, 0] += wall_repulsion_strength * overlap_left
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii_ft[i]) - 1.0
+ if overlap_right > 0:
+ forces_ft[i, 0] -= wall_repulsion_strength * overlap_right
+ overlap_bottom = pressured_radii_ft[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces_ft[i, 1] += wall_repulsion_strength * overlap_bottom
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii_ft[i]) - 1.0
+ if overlap_top > 0:
+ forces_ft[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..2f6ffd3604aa713649f54f95179b7d99eaabbc97
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results
+Run 1/1 completed in 1430.16 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9636401099688428
+ public: {'centers_str': ' centers[0] = (0.0871, 0.0882)\n centers[1] = (0.3158, 0.0842)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8985, 0.1015)\n centers[5] = (0.0807, 0.3194)\n centers[6] = (0.3106, 0.3117)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.6994, 0.4845)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.2838, 0.6998)\n centers[17] = (0.4680, 0.6950)\n centers[18] = (0.7133, 0.6888)\n centers[19] = (0.9277, 0.7011)\n centers[20] = (0.0938, 0.8998)\n centers[21] = (0.2973, 0.8907)\n centers[22] = (0.5104, 0.8971)\n centers[23] = (0.7036, 0.9156)\n centers[24] = (0.8953, 0.8952)\n centers[25] = (0.2059, 0.1965)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9636401099688428}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/packing_viz.png
+ execution_time_mean: 1430.1595351309516
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..c5867aba7456c9ededb67a778afe56491933a69d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9636401099688428,
+ "public": {
+ "centers_str": " centers[0] = (0.0871, 0.0882)\n centers[1] = (0.3158, 0.0842)\n centers[2] = (0.5000, 0.1000)\n centers[3] = (0.7000, 0.1000)\n centers[4] = (0.8985, 0.1015)\n centers[5] = (0.0807, 0.3194)\n centers[6] = (0.3106, 0.3117)\n centers[7] = (0.5000, 0.3000)\n centers[8] = (0.7000, 0.3000)\n centers[9] = (0.9000, 0.3000)\n centers[10] = (0.1000, 0.5000)\n centers[11] = (0.3000, 0.5000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.6994, 0.4845)\n centers[14] = (0.9000, 0.5000)\n centers[15] = (0.1000, 0.7000)\n centers[16] = (0.2838, 0.6998)\n centers[17] = (0.4680, 0.6950)\n centers[18] = (0.7133, 0.6888)\n centers[19] = (0.9277, 0.7011)\n centers[20] = (0.0938, 0.8998)\n centers[21] = (0.2973, 0.8907)\n centers[22] = (0.5104, 0.8971)\n centers[23] = (0.7036, 0.9156)\n centers[24] = (0.8953, 0.8952)\n centers[25] = (0.2059, 0.1965)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9636401099688428
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/results/packing_viz.png",
+ "execution_time_mean": 1430.1595351309516,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..756dd6c904dc9e4e84027c17e08c8e6f46337c5d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_69/search_replace.txt
@@ -0,0 +1,221 @@
+The current implementation of the `construct_packing` function relies on nested Python loops for calculating inter-circle and wall repulsion forces during the simulation phases. This approach is computationally expensive, significantly limiting the number of iterations that can be performed within a reasonable timeframe. Previous higher-scoring programs utilized vectorized NumPy operations for these force calculations, which drastically improves performance.
+
+The primary improvement will be to replace these explicit loops with vectorized NumPy equivalents. This change will allow the simulation to run much faster, enabling an increase in the number of simulation iterations and refined tuning of other hyperparameters, ultimately leading to better packing solutions.
+
+After vectorizing, I will also adjust some hyperparameters to take advantage of the increased simulation speed.
+
+**Reasoning for Vectorization:**
+* **Performance**: NumPy's vectorized operations are implemented in C and are significantly faster than Python loops for array manipulations. This is especially critical for simulations involving many particles (circles) where pairwise interactions are calculated in each step.
+* **Scalability**: As the number of circles `n` increases, the cost of nested loops grows quadratically (`O(n^2)`), whereas vectorized operations can often handle this more efficiently due to optimized underlying implementations.
+
+**Reasoning for Hyperparameter Adjustment (after vectorization):**
+* **Increased Iterations**: With faster force calculations, the `main_iterations` and `fine_tune_iterations` can be safely increased. More iterations allow the circles to explore the solution space more thoroughly and settle into more optimal configurations.
+* **Adjusted Learning Rates**: Slightly higher learning rates can be used in the earlier stages of the simulation to encourage broader exploration, while maintaining small rates for fine-tuning to ensure precision.
+* **Growth Pressure**: Modifying the `initial_growth_pressure` can help ensure the initial grid "melts" effectively, and increasing the iterations of `compute_radii_for_simulation` will lead to more accurate radii calculations within each simulation step.
+
+
+vectorize_main_forces
+
+
+
+This edit vectorizes the circle-to-circle and wall repulsion force calculations within the main simulation loop. This will significantly improve the performance of the simulation by leveraging NumPy's optimized array operations, allowing for more iterations and better exploration of the solution space.
+
+
+
+<<<<<<< SEARCH
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+=======
+ forces = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ # Avoid division by zero for identical centers, resulting in zero force
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces += wall_forces # Add wall forces to total forces
+>>>>>>> REPLACE
+
+
+
+vectorize_finetune_forces
+
+
+
+This edit vectorizes the circle-to-circle and wall repulsion force calculations within the fine-tuning loop. Similar to the main simulation loop, this optimization will significantly speed up the fine-tuning phase, allowing for more precise adjustments and better convergence to an optimal packing.
+
+
+
+<<<<<<< SEARCH
+ forces_ft = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ overlap = pressured_radii_ft[i] + pressured_radii_ft[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces_ft[i] += force_vec
+ forces_ft[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ overlap_left = pressured_radii_ft[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces_ft[i, 0] += wall_repulsion_strength * overlap_left
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii_ft[i]) - 1.0
+ if overlap_right > 0:
+ forces_ft[i, 0] -= wall_repulsion_strength * overlap_right
+ overlap_bottom = pressured_radii_ft[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces_ft[i, 1] += wall_repulsion_strength * overlap_bottom
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii_ft[i]) - 1.0
+ if overlap_top > 0:
+ forces_ft[i, 1] -= wall_repulsion_strength * overlap_top
+=======
+ forces_ft = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces_ft += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces_ft = np.zeros_like(centers_for_sim)
+ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+
+ forces_ft += wall_forces_ft
+>>>>>>> REPLACE
+
+
+
+tune_simulation_parameters
+
+
+
+With the force calculations vectorized, the simulation runs much faster. This edit increases the number of iterations for both the main and fine-tuning phases, and also adjusts learning rates and growth pressures to leverage the increased computational budget, aiming for better convergence and higher total radii. It also increases iterations for `compute_radii_for_simulation` for better accuracy during the simulation.
+
+
+
+<<<<<<< SEARCH
+ main_iterations = 1200 # Number of steps for main force-directed simulation
+ main_learning_rate = 0.01 # Initial learning rate for main phase
+ wall_repulsion_strength = 0.35 # Balanced wall repulsion to utilize boundaries effectively
+ initial_growth_pressure = 1.018 # Stronger initial growth pressure for aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+ fine_tune_iterations = 300 # Number of steps for fine-tuning
+ fine_tune_learning_rate = 0.0008 # Very low learning rate for precise adjustments
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure for fine-tuning
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+=======
+ main_iterations = 2500 # Increased iterations for more thorough exploration after vectorization
+ main_learning_rate = 0.015 # Slightly increased initial learning rate for faster initial movements
+ wall_repulsion_strength = 0.4 # Slightly stronger wall repulsion to better contain circles and utilize boundaries
+ initial_growth_pressure = 1.03 # Increased initial growth pressure for more aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+ fine_tune_iterations = 1000 # Increased fine-tune iterations for more precise settling
+ fine_tune_learning_rate = 0.001 # Slightly increased learning rate for fine-tuning to allow more movement
+ finetune_growth_pressure_start = 1.0008 # Subtle growth pressure for fine-tuning, slightly stronger nudge
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+>>>>>>> REPLACE
+
+
+
+increase_sim_radii_accuracy
+
+
+
+The `compute_radii_for_simulation` function is called frequently within the simulation loops. With vectorized force calculations speeding up the main loops, we can afford to increase the iteration count for `compute_radii_for_simulation`. This ensures more accurate radius calculations within each simulation step, which can lead to better force estimations and a more optimal final packing.
+
+
+
+<<<<<<< SEARCH
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+=======
+ # Increased iterations for more accurate radii during simulation, leveraging faster force computations.
+ for _ in range(400):
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5bf8301d7e3fad53f2bc54ffbb21fe0c59fcf39f
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..474ebc22951d5363fc670223487ae712a2cd2e85
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/edit.diff
@@ -0,0 +1,138 @@
+--- a/original.py
++++ b/original.py
+@@ -1,95 +1,111 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+- Tuple of (centers, radii, sum_of_radii)
++ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+- sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+- # Place 25 circles in a 5x5 grid
+- # This provides a more even distribution and better use of space
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
++ # --- New Initial Placement Strategy ---
++ # Arrange 24 circles in a 6x4 grid
++ num_x_cells = 6
++ num_y_cells = 4
++
++ spacing_x = 1.0 / num_x_cells
++ spacing_y = 1.0 / num_y_cells
+
+ k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- centers[k, 0] = (i + 0.5) * spacing
+- centers[k, 1] = (j + 0.5) * spacing
++ for j in range(num_y_cells):
++ for i in range(num_x_cells):
++ centers[k, 0] = (i + 0.5) * spacing_x
++ centers[k, 1] = (j + 0.5) * spacing_y
+ k += 1
+
+- # Place the 26th circle in a corner, slightly offset to allow for radius.
+- # This location is chosen to utilize corner space efficiently,
+- # as the grid circles are not directly in the corners.
+- centers[25] = [0.05, 0.05]
++ # Place the 25th circle at the center of the square
++ centers[24] = [0.5, 0.5]
+
++ # Place the 26th circle at a quarter-point for better overall balance
++ centers[25] = [0.25, 0.25]
++
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+- # Then, limit by distance to other circles
+- # Each pair of circles with centers at distance d can have
+- # sum of radii at most d to avoid overlap
+- # This iterative scaling is a greedy approach and the order can matter,
+- # but for constructor-based approaches, it's a common method.
+- for _ in range(100): # Iterate multiple times to stabilize radii
++ # Then, limit by distance to other circles using iterative proportional scaling
++ # Increased iterations for better convergence
++ for _ in range(200): # Iterate more times to stabilize radii
++ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+- dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
++ dist = np.linalg.norm(centers[i] - centers[j])
+
+- # If current radii would cause overlap
++ # If current radii would cause overlap and distance is not zero
++ if dist < 1e-9: # Handle cases where centers might be identical or very close
++ # If centers are practically the same, one circle must have radius 0
++ # or handle based on problem specifics (e.g., allow one small, one 0)
++ # For packing, it's safer to consider them as overlapping severely
++ # and shrink them
++ radii[i] = 0
++ radii[j] = 0
++ updated_in_pass = True
++ continue
++
+ if radii[i] + radii[j] > dist:
++ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+- return radii
+-
++
++ # If a full pass over all pairs results in no change, radii have stabilized
++ if not updated_in_pass:
++ break
++
++ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b2ee7b8d439ea3c13370e26d4c18f191154f4b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/main.py
@@ -0,0 +1,111 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 24 circles in a 6x4 grid
+ num_x_cells = 6
+ num_y_cells = 4
+
+ spacing_x = 1.0 / num_x_cells
+ spacing_y = 1.0 / num_y_cells
+
+ k = 0
+ for j in range(num_y_cells):
+ for i in range(num_x_cells):
+ centers[k, 0] = (i + 0.5) * spacing_x
+ centers[k, 1] = (j + 0.5) * spacing_y
+ k += 1
+
+ # Place the 25th circle at the center of the square
+ centers[24] = [0.5, 0.5]
+
+ # Place the 26th circle at a quarter-point for better overall balance
+ centers[25] = [0.25, 0.25]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(200): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # or handle based on problem specifics (e.g., allow one small, one 0)
+ # For packing, it's safer to consider them as overlapping severely
+ # and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..00c138de9a06c6d9335089fddee91aec818e5abd
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/original.py
@@ -0,0 +1,95 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place 25 circles in a 5x5 grid
+ # This provides a more even distribution and better use of space
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Place the 26th circle in a corner, slightly offset to allow for radius.
+ # This location is chosen to utilize corner space efficiently,
+ # as the grid circles are not directly in the corners.
+ centers[25] = [0.05, 0.05]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ # This iterative scaling is a greedy approach and the order can matter,
+ # but for constructor-based approaches, it's a common method.
+ for _ in range(100): # Iterate multiple times to stabilize radii
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..a02f965f861532c8b7c1f5d63a111cd0703746e7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results
+Run 1/1 completed in 0.00 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.589467413685812
+ public: {'centers_str': ' centers[0] = (0.0833, 0.1250)\n centers[1] = (0.2500, 0.1250)\n centers[2] = (0.4167, 0.1250)\n centers[3] = (0.5833, 0.1250)\n centers[4] = (0.7500, 0.1250)\n centers[5] = (0.9167, 0.1250)\n centers[6] = (0.0833, 0.3750)\n centers[7] = (0.2500, 0.3750)\n centers[8] = (0.4167, 0.3750)\n centers[9] = (0.5833, 0.3750)\n centers[10] = (0.7500, 0.3750)\n centers[11] = (0.9167, 0.3750)\n centers[12] = (0.0833, 0.6250)\n centers[13] = (0.2500, 0.6250)\n centers[14] = (0.4167, 0.6250)\n centers[15] = (0.5833, 0.6250)\n centers[16] = (0.7500, 0.6250)\n centers[17] = (0.9167, 0.6250)\n centers[18] = (0.0833, 0.8750)\n centers[19] = (0.2500, 0.8750)\n centers[20] = (0.4167, 0.8750)\n centers[21] = (0.5833, 0.8750)\n centers[22] = (0.7500, 0.8750)\n centers[23] = (0.9167, 0.8750)\n centers[24] = (0.5000, 0.5000)\n centers[25] = (0.2500, 0.2500)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.589467413685812}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/packing_viz.png
+ execution_time_mean: 0.0022423849441111088
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..51e6cc16819b107f9335f937d296f22290a5a1b3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.589467413685812,
+ "public": {
+ "centers_str": " centers[0] = (0.0833, 0.1250)\n centers[1] = (0.2500, 0.1250)\n centers[2] = (0.4167, 0.1250)\n centers[3] = (0.5833, 0.1250)\n centers[4] = (0.7500, 0.1250)\n centers[5] = (0.9167, 0.1250)\n centers[6] = (0.0833, 0.3750)\n centers[7] = (0.2500, 0.3750)\n centers[8] = (0.4167, 0.3750)\n centers[9] = (0.5833, 0.3750)\n centers[10] = (0.7500, 0.3750)\n centers[11] = (0.9167, 0.3750)\n centers[12] = (0.0833, 0.6250)\n centers[13] = (0.2500, 0.6250)\n centers[14] = (0.4167, 0.6250)\n centers[15] = (0.5833, 0.6250)\n centers[16] = (0.7500, 0.6250)\n centers[17] = (0.9167, 0.6250)\n centers[18] = (0.0833, 0.8750)\n centers[19] = (0.2500, 0.8750)\n centers[20] = (0.4167, 0.8750)\n centers[21] = (0.5833, 0.8750)\n centers[22] = (0.7500, 0.8750)\n centers[23] = (0.9167, 0.8750)\n centers[24] = (0.5000, 0.5000)\n centers[25] = (0.2500, 0.2500)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.589467413685812
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/results/packing_viz.png",
+ "execution_time_mean": 0.0022423849441111088,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4b2ee7b8d439ea3c13370e26d4c18f191154f4b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_7/rewrite.txt
@@ -0,0 +1,111 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # --- New Initial Placement Strategy ---
+ # Arrange 24 circles in a 6x4 grid
+ num_x_cells = 6
+ num_y_cells = 4
+
+ spacing_x = 1.0 / num_x_cells
+ spacing_y = 1.0 / num_y_cells
+
+ k = 0
+ for j in range(num_y_cells):
+ for i in range(num_x_cells):
+ centers[k, 0] = (i + 0.5) * spacing_x
+ centers[k, 1] = (j + 0.5) * spacing_y
+ k += 1
+
+ # Place the 25th circle at the center of the square
+ centers[24] = [0.5, 0.5]
+
+ # Place the 26th circle at a quarter-point for better overall balance
+ centers[25] = [0.25, 0.25]
+
+ # No need to clip centers, as they are intentionally placed within (0,1) range
+ # and compute_max_radii handles boundary constraints.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence
+ for _ in range(200): # Iterate more times to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, one circle must have radius 0
+ # or handle based on problem specifics (e.g., allow one small, one 0)
+ # For packing, it's safer to consider them as overlapping severely
+ # and shrink them
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..61b7971fb356217cb982dfc7ac9c6c6b58ba10f6
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..5ba6402c33b63bd3ef54f18318f6faf51b04b4e9
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/edit.diff
@@ -0,0 +1,343 @@
+--- a/original.py
++++ b/original.py
+@@ -1,218 +1,199 @@
+ # EVOLVE-BLOCK-START
+-"""
+-Physical simulation-based circle packing for n=26 circles.
+-This algorithm is a crossover, combining the high-level strategy of the best-performing
+-parent (multi-start, two-phase simulation, annealed growth pressure) with the
+-efficient vectorized force calculations from the inspiration program.
+-"""
++import numpy as np
+
+-import numpy as np
++def compute_max_radii(centers, max_iter=1000, convergence_threshold=1e-8):
++ """
++ Compute maximum radii using iterative proportional scaling with a robust convergence check.
++ This is an improvement over a fixed number of iterations.
++ """
++ n = centers.shape[0]
++ if n == 0:
++ return np.array([])
++
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ for _ in range(max_iter):
++ old_radii = radii.copy()
++
++ updated = False
++ for i in range(n):
++ for j in range(i + 1, n):
++ dist = np.linalg.norm(centers[i] - centers[j])
++ if radii[i] + radii[j] > dist:
++ if dist < 1e-9: # Handle co-located centers
++ radii[i], radii[j] = 0.0, 0.0
++ else:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++
++ # Check for convergence based on the maximum change in any radius
++ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
++ break
++
++ return np.maximum(radii, 0)
++
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles by simulating physical repulsion forces
+- to find an optimal arrangement of centers.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
++ Constructs a packing of 26 circles by running multiple trials of an aggressive,
++ two-phase physical simulation and selecting the best result.
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+- # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+- iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
+- learning_rate = 0.015 # Increased learning rate for more significant center movements
+- wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
+- initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
+- final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
++ # More aggressive parameters for a stronger "explosion" to break grid symmetry
++ iterations = 2500
++ learning_rate = 0.02
++ wall_repulsion_strength = 0.6
++ initial_growth_pressure = 1.08 # Significantly higher pressure for wider exploration
++ final_growth_pressure = 1.0001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+- # Candidate initial positions for the 26th circle to optimize corner utilization.
+- candidate_extra_circle_initial_positions = [
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+- [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
++ # --- Hybrid Initialization Strategy ---
++ # 1. Grid-based initial positions for the 26th circle
++ candidate_extra_circle_positions = [
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
++ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Primary interstitial
++ [0.5, 0.2], [0.2, 0.5], [0.8, 0.5], [0.5, 0.8], # Mid-edge interstitial
++ [0.4, 0.4], [0.6, 0.6], [0.4, 0.6], [0.6, 0.4] # Central interstitial
+ ]
++ # 2. Add trials with fully random starts for diversity
++ num_random_trials = 4
++ total_trials = len(candidate_extra_circle_positions) + num_random_trials
+
+- for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
++ for trial_idx in range(total_trials):
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- current_centers[k, 0] = (i + 0.5) * spacing
+- current_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+
+- # Add small random perturbation to the 5x5 grid (first 25 circles, re-introduced from successful prior)
+- perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
+- current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
++ if trial_idx < len(candidate_extra_circle_positions):
++ # Grid-based start
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for i in range(num_cells_side):
++ for j in range(num_cells_side):
++ current_centers[k, 0] = (i + 0.5) * spacing
++ current_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ # Larger perturbation to break symmetry more effectively
++ perturbation_scale = 0.01
++ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
++ current_centers[25] = candidate_extra_circle_positions[trial_idx]
++ else:
++ # Fully random start
++ current_centers = np.random.rand(n, 2)
+
+- current_centers[25] = initial_pos_26th_circle
+-
+- # Ensure all centers stay within bounds after initial setup and perturbation
+- current_centers = np.clip(current_centers, 0.0, 1.0)
+-
+- centers_for_sim = current_centers.copy()
++ centers_for_sim = np.clip(current_centers, 0.0, 1.0)
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+- radii = compute_max_radii(centers_for_sim)
++ # Use the improved radius calculation with fewer iterations for speed
++ radii = compute_max_radii(centers_for_sim, max_iter=250)
+
+- # Anneal growth pressure quadratically
++ # Quadratic annealing for growth pressure
++ progress = sim_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * \
+- (1.0 - (sim_iter / iterations))**2
++ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ inflated_radii = radii * current_growth_pressure
+
+- # --- Vectorized Force Calculation ---
+- # a. Circle-to-circle repulsion forces
++ # Vectorized force calculations
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+-
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ force_mags = overlaps / (dists + 1e-9)
++ force_matrix = diffs * force_mags[..., np.newaxis]
+
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+- # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = inflated_radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+- overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
++ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, inflated_radii - centers_for_sim[:, 0])
++ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + inflated_radii) - 1.0)
++ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, inflated_radii - centers_for_sim[:, 1])
++ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+- # --- Update center positions ---
+- current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
++ # Update center positions with annealed learning rate
++ current_lr = learning_rate * (1.0 - progress)**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+- # --- Finalization for current trial ---
++ # Check if this trial produced a better result
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+- # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+- fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+- finetune_growth_pressure_start = 1.0005 # Subtle growth pressure
+- finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
++ # --- Post-Simulation Fine-Tuning Phase (on the best result so far) ---
++ fine_tune_iterations = 1000
++ fine_tune_learning_rate = 0.001
++ finetune_growth_pressure_start = 1.001
++ finetune_growth_pressure_end = 1.00001
+ centers_for_sim = best_final_centers.copy()
+
+- for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+- radii = compute_max_radii(centers_for_sim)
++ for sim_iter_ft in range(fine_tune_iterations):
++ radii = compute_max_radii(centers_for_sim, max_iter=250)
+
+- # Anneal subtle growth pressure (linear decay for stability)
+- current_finetune_growth_pressure = finetune_growth_pressure_start - \
+- (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+- (sim_iter_ft / fine_tune_iterations)
+- inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
++ progress = sim_iter_ft / fine_tune_iterations
++ current_finetune_growth_pressure = finetune_growth_pressure_end + \
++ (finetune_growth_pressure_start - finetune_growth_pressure_end) * (1.0 - progress)
++ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+- # --- Vectorized Force Calculation ---
+- # a. Circle-to-circle forces
++ # Vectorized force calculation
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+-
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++
++ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ force_mags = overlaps / (dists + 1e-9)
++ force_matrix = diffs * force_mags[..., np.newaxis]
+
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+- # b. Wall forces
++ # BUG FIX: Wall forces must use the same inflated radii as circle forces
+ wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+- overlap_bottom = radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
++ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, inflated_radii_ft - centers_for_sim[:, 0])
++ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0)
++ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, inflated_radii_ft - centers_for_sim[:, 1])
++ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+- # Anneal learning rate for fine-tuning (linear decay)
+- current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
++ # Anneal learning rate for fine-tuning
++ current_finetune_lr = fine_tune_learning_rate * (1.0 - progress)
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
++ # Final, high-precision radius calculation
+ final_centers = centers_for_sim
+- final_radii = compute_max_radii(final_centers)
++ final_radii = compute_max_radii(final_centers, max_iter=2000)
+
+ return final_centers, final_radii
+-
+-
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This uses an iterative proportional scaling method.
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- for _ in range(500):
+- updated_in_pass = False
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9:
+- radii[i] = 0.0
+- radii[j] = 0.0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0.0)
+-
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ff6e2efc300862b5ad400c0d1d016e2a9a9896e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/main.py
@@ -0,0 +1,199 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=1000, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a robust convergence check.
+ This is an improvement over a fixed number of iterations.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence based on the maximum change in any radius
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by running multiple trials of an aggressive,
+ two-phase physical simulation and selecting the best result.
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # More aggressive parameters for a stronger "explosion" to break grid symmetry
+ iterations = 2500
+ learning_rate = 0.02
+ wall_repulsion_strength = 0.6
+ initial_growth_pressure = 1.08 # Significantly higher pressure for wider exploration
+ final_growth_pressure = 1.0001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # --- Hybrid Initialization Strategy ---
+ # 1. Grid-based initial positions for the 26th circle
+ candidate_extra_circle_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Primary interstitial
+ [0.5, 0.2], [0.2, 0.5], [0.8, 0.5], [0.5, 0.8], # Mid-edge interstitial
+ [0.4, 0.4], [0.6, 0.6], [0.4, 0.6], [0.6, 0.4] # Central interstitial
+ ]
+ # 2. Add trials with fully random starts for diversity
+ num_random_trials = 4
+ total_trials = len(candidate_extra_circle_positions) + num_random_trials
+
+ for trial_idx in range(total_trials):
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+
+ if trial_idx < len(candidate_extra_circle_positions):
+ # Grid-based start
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Larger perturbation to break symmetry more effectively
+ perturbation_scale = 0.01
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+ current_centers[25] = candidate_extra_circle_positions[trial_idx]
+ else:
+ # Fully random start
+ current_centers = np.random.rand(n, 2)
+
+ centers_for_sim = np.clip(current_centers, 0.0, 1.0)
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ # Use the improved radius calculation with fewer iterations for speed
+ radii = compute_max_radii(centers_for_sim, max_iter=250)
+
+ # Quadratic annealing for growth pressure
+ progress = sim_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ inflated_radii = radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ force_mags = overlaps / (dists + 1e-9)
+ force_matrix = diffs * force_mags[..., np.newaxis]
+
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, inflated_radii - centers_for_sim[:, 0])
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, inflated_radii - centers_for_sim[:, 1])
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ # Update center positions with annealed learning rate
+ current_lr = learning_rate * (1.0 - progress)**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # Check if this trial produced a better result
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (on the best result so far) ---
+ fine_tune_iterations = 1000
+ fine_tune_learning_rate = 0.001
+ finetune_growth_pressure_start = 1.001
+ finetune_growth_pressure_end = 1.00001
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim, max_iter=250)
+
+ progress = sim_iter_ft / fine_tune_iterations
+ current_finetune_growth_pressure = finetune_growth_pressure_end + \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * (1.0 - progress)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # Vectorized force calculation
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ force_mags = overlaps / (dists + 1e-9)
+ force_matrix = diffs * force_mags[..., np.newaxis]
+
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # BUG FIX: Wall forces must use the same inflated radii as circle forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, inflated_radii_ft - centers_for_sim[:, 0])
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, inflated_radii_ft - centers_for_sim[:, 1])
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - progress)
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # Final, high-precision radius calculation
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers, max_iter=2000)
+
+ return final_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..639813b26fb788c17c10286874fceb9ed6335512
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/original.py
@@ -0,0 +1,218 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+ iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
+ learning_rate = 0.015 # Increased learning rate for more significant center movements
+ wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
+ initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, re-introduced from successful prior)
+ perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..1fe2cb3dc237852859d5a9b41d02370222d8bed6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results
+Run 1/1 completed in 112.54 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.934240135650011
+ public: {'centers_str': ' centers[0] = (0.1008, 0.0984)\n centers[1] = (0.0991, 0.3013)\n centers[2] = (0.1000, 0.5024)\n centers[3] = (0.0988, 0.6974)\n centers[4] = (0.0942, 0.8975)\n centers[5] = (0.2977, 0.1025)\n centers[6] = (0.2202, 0.2227)\n centers[7] = (0.2551, 0.5538)\n centers[8] = (0.3004, 0.7000)\n centers[9] = (0.2971, 0.8869)\n centers[10] = (0.4992, 0.1022)\n centers[11] = (0.5476, 0.2528)\n centers[12] = (0.5536, 0.5408)\n centers[13] = (0.4343, 0.6943)\n centers[14] = (0.4829, 0.9194)\n centers[15] = (0.7002, 0.0789)\n centers[16] = (0.6683, 0.2360)\n centers[17] = (0.6817, 0.4297)\n centers[18] = (0.6979, 0.7420)\n centers[19] = (0.7106, 0.9695)\n centers[20] = (0.9023, 0.0824)\n centers[21] = (0.8709, 0.2927)\n centers[22] = (0.8997, 0.5216)\n centers[23] = (0.9465, 0.7052)\n centers[24] = (0.9171, 0.9167)\n centers[25] = (0.3835, 0.3830)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.934240135650011}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/packing_viz.png
+ execution_time_mean: 112.54262197809294
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..02e1a018e674bc2e0ddcf7bc64bfc4907a29b1fb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.934240135650011,
+ "public": {
+ "centers_str": " centers[0] = (0.1008, 0.0984)\n centers[1] = (0.0991, 0.3013)\n centers[2] = (0.1000, 0.5024)\n centers[3] = (0.0988, 0.6974)\n centers[4] = (0.0942, 0.8975)\n centers[5] = (0.2977, 0.1025)\n centers[6] = (0.2202, 0.2227)\n centers[7] = (0.2551, 0.5538)\n centers[8] = (0.3004, 0.7000)\n centers[9] = (0.2971, 0.8869)\n centers[10] = (0.4992, 0.1022)\n centers[11] = (0.5476, 0.2528)\n centers[12] = (0.5536, 0.5408)\n centers[13] = (0.4343, 0.6943)\n centers[14] = (0.4829, 0.9194)\n centers[15] = (0.7002, 0.0789)\n centers[16] = (0.6683, 0.2360)\n centers[17] = (0.6817, 0.4297)\n centers[18] = (0.6979, 0.7420)\n centers[19] = (0.7106, 0.9695)\n centers[20] = (0.9023, 0.0824)\n centers[21] = (0.8709, 0.2927)\n centers[22] = (0.8997, 0.5216)\n centers[23] = (0.9465, 0.7052)\n centers[24] = (0.9171, 0.9167)\n centers[25] = (0.3835, 0.3830)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.934240135650011
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/results/packing_viz.png",
+ "execution_time_mean": 112.54262197809294,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..bffc23bf8cb15041054e5dcd56ff889fd5df5ab5
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_70/rewrite.txt
@@ -0,0 +1,189 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=1000, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a robust convergence check.
+ This is an improvement over a fixed number of iterations.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence based on the maximum change in any radius
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by running multiple trials of an aggressive,
+ two-phase physical simulation and selecting the best result.
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # More aggressive parameters for a stronger "explosion" to break grid symmetry
+ iterations = 2500
+ learning_rate = 0.02
+ wall_repulsion_strength = 0.6
+ initial_growth_pressure = 1.08 # Significantly higher pressure for wider exploration
+ final_growth_pressure = 1.0001
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # --- Hybrid Initialization Strategy ---
+ # 1. Grid-based initial positions for the 26th circle
+ candidate_extra_circle_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Primary interstitial
+ [0.5, 0.2], [0.2, 0.5], [0.8, 0.5], [0.5, 0.8], # Mid-edge interstitial
+ [0.4, 0.4], [0.6, 0.6], [0.4, 0.6], [0.6, 0.4] # Central interstitial
+ ]
+ # 2. Add trials with fully random starts for diversity
+ num_random_trials = 4
+ total_trials = len(candidate_extra_circle_positions) + num_random_trials
+
+ for trial_idx in range(total_trials):
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+
+ if trial_idx < len(candidate_extra_circle_positions):
+ # Grid-based start
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Larger perturbation to break symmetry more effectively
+ perturbation_scale = 0.01
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+ current_centers[25] = candidate_extra_circle_positions[trial_idx]
+ else:
+ # Fully random start
+ current_centers = np.random.rand(n, 2)
+
+ centers_for_sim = np.clip(current_centers, 0.0, 1.0)
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ # Use the improved radius calculation with fewer iterations for speed
+ radii = compute_max_radii(centers_for_sim, max_iter=250)
+
+ # Quadratic annealing for growth pressure
+ progress = sim_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ inflated_radii = radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ force_mags = overlaps / (dists + 1e-9)
+ force_matrix = diffs * force_mags[..., np.newaxis]
+
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, inflated_radii - centers_for_sim[:, 0])
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, inflated_radii - centers_for_sim[:, 1])
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ # Update center positions with annealed learning rate
+ current_lr = learning_rate * (1.0 - progress)**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # Check if this trial produced a better result
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (on the best result so far) ---
+ fine_tune_iterations = 1000
+ fine_tune_learning_rate = 0.001
+ finetune_growth_pressure_start = 1.001
+ finetune_growth_pressure_end = 1.00001
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim, max_iter=250)
+
+ progress = sim_iter_ft / fine_tune_iterations
+ current_finetune_growth_pressure = finetune_growth_pressure_end + \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * (1.0 - progress)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # Vectorized force calculation
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ force_mags = overlaps / (dists + 1e-9)
+ force_matrix = diffs * force_mags[..., np.newaxis]
+
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # BUG FIX: Wall forces must use the same inflated radii as circle forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, inflated_radii_ft - centers_for_sim[:, 0])
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, inflated_radii_ft - centers_for_sim[:, 1])
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - progress)
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # Final, high-precision radius calculation
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers, max_iter=2000)
+
+ return final_centers, final_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..67f862491d1965fa8b7fd7af455592d79c48e33e
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..e619fabddb0fbfef63940fc80966183d0bc6db68
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/edit.diff
@@ -0,0 +1,319 @@
+--- a/original.py
++++ b/original.py
+@@ -1,285 +1,298 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+- # --- Hyperparameters for the simulation phases ---
+- sim_iter = 2000
+- learning_rate = 0.02
+- wall_strength = 0.8
+- initial_growth_pressure = 1.02
+- final_growth_pressure = 1.001
+- fine_tune_iter = 400
+- fine_tune_lr = 0.001
+- radius_sim_iter = 75 # Iterations for compute_max_radii during simulation (for speed)
++ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
++ sim_iter = 2500 # Increased iterations for more thorough refinement.
++ learning_rate = 0.02 # A balanced learning rate for exploration.
++ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
++ initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
++ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
++ fine_tune_iter = 600 # Increased iterations for final settlement.
++ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
++ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
++ # Add a small perturbation to the base grid to break symmetry and encourage rearrangement.
++ perturbation_strength = 0.01 * spacing # 1% of the grid spacing
++
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+- # Create a new 'centers' array for this trial to avoid modifying base_25_centers
++ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+- current_centers_trial[:n-1] = base_25_centers.copy()
++
++ # Start with a fresh copy of the base grid and perturb it
++ perturbed_grid = base_25_centers.copy()
++ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
++ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
++
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+ def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ ):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+ def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+- # The number of iterations is parameterized for performance tuning.
++ # This loop is more robust, checking for convergence based on minimal changes.
++ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
++
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
++ previous_radii = radii.copy()
++
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+- # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
++ if radii[i] > 0 or radii[j] > 0:
++ radii[i] = 0.0
++ radii[j] = 0.0
++ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+- # If a full pass over all pairs results in no change, radii have stabilized.
+- if not updated_in_pass:
++ # Check for convergence: if no updates were made AND the max change is tiny.
++ max_delta_r = np.max(np.abs(radii - previous_radii))
++ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..37d063913f173f566ad1e59630b3135347d80321
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/main.py
@@ -0,0 +1,298 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # Add a small perturbation to the base grid to break symmetry and encourage rearrangement.
+ perturbation_strength = 0.01 * spacing # 1% of the grid spacing
+
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+
+ # Start with a fresh copy of the base grid and perturb it
+ perturbed_grid = base_25_centers.copy()
+ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d8df5ad4c2de9e8724c08e572ff599e893251eea
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/original.py
@@ -0,0 +1,285 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases ---
+ sim_iter = 2000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 400
+ fine_tune_lr = 0.001
+ radius_sim_iter = 75 # Iterations for compute_max_radii during simulation (for speed)
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ # Create a new 'centers' array for this trial to avoid modifying base_25_centers
+ current_centers_trial = np.zeros((n, 2))
+ current_centers_trial[:n-1] = base_25_centers.copy()
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # The number of iterations is parameterized for performance tuning.
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..9c13d91968604edafeb3d320c1114c70d1fe3286
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results
+Run 1/1 completed in 103.09 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9670497686053354
+ public: {'centers_str': ' centers[0] = (0.0735, 0.0797)\n centers[1] = (0.3244, 0.0735)\n centers[2] = (0.5018, 0.1003)\n centers[3] = (0.7002, 0.1036)\n centers[4] = (0.8981, 0.1019)\n centers[5] = (0.0708, 0.3256)\n centers[6] = (0.3186, 0.3231)\n centers[7] = (0.4981, 0.2983)\n centers[8] = (0.7023, 0.2987)\n centers[9] = (0.9002, 0.3017)\n centers[10] = (0.1036, 0.4988)\n centers[11] = (0.2974, 0.5010)\n centers[12] = (0.4954, 0.4996)\n centers[13] = (0.7026, 0.4764)\n centers[14] = (0.8989, 0.5019)\n centers[15] = (0.0999, 0.7028)\n centers[16] = (0.2763, 0.6881)\n centers[17] = (0.4584, 0.6820)\n centers[18] = (0.7078, 0.6898)\n centers[19] = (0.9283, 0.7004)\n centers[20] = (0.0875, 0.8989)\n centers[21] = (0.2901, 0.8849)\n centers[22] = (0.5185, 0.8872)\n centers[23] = (0.7103, 0.9195)\n centers[24] = (0.8949, 0.8948)\n centers[25] = (0.2076, 0.1979)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9670497686053354}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/packing_viz.png
+ execution_time_mean: 103.09052400896326
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..fdb2ad44894f4390889578d355f9937f04b445e8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9670497686053354,
+ "public": {
+ "centers_str": " centers[0] = (0.0735, 0.0797)\n centers[1] = (0.3244, 0.0735)\n centers[2] = (0.5018, 0.1003)\n centers[3] = (0.7002, 0.1036)\n centers[4] = (0.8981, 0.1019)\n centers[5] = (0.0708, 0.3256)\n centers[6] = (0.3186, 0.3231)\n centers[7] = (0.4981, 0.2983)\n centers[8] = (0.7023, 0.2987)\n centers[9] = (0.9002, 0.3017)\n centers[10] = (0.1036, 0.4988)\n centers[11] = (0.2974, 0.5010)\n centers[12] = (0.4954, 0.4996)\n centers[13] = (0.7026, 0.4764)\n centers[14] = (0.8989, 0.5019)\n centers[15] = (0.0999, 0.7028)\n centers[16] = (0.2763, 0.6881)\n centers[17] = (0.4584, 0.6820)\n centers[18] = (0.7078, 0.6898)\n centers[19] = (0.9283, 0.7004)\n centers[20] = (0.0875, 0.8989)\n centers[21] = (0.2901, 0.8849)\n centers[22] = (0.5185, 0.8872)\n centers[23] = (0.7103, 0.9195)\n centers[24] = (0.8949, 0.8948)\n centers[25] = (0.2076, 0.1979)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9670497686053354
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/results/packing_viz.png",
+ "execution_time_mean": 103.09052400896326,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ac0d123e46b390823df2cf285f30e71157b22008
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_71/search_replace.txt
@@ -0,0 +1,150 @@
+My analysis indicates that the current program is underperforming because its physical simulation is getting trapped in a local optimum near the highly symmetric initial grid configuration. The final packing is too orderly and contains significant wasted space.
+
+To address this, I will re-introduce and enhance three successful strategies from previous high-scoring implementations:
+
+1. **Increase Simulation Intensity:** I will increase the number of simulation iterations and the initial "growth pressure." This provides the system with more time and energy to "melt" the initial grid and explore more complex, non-grid-like arrangements.
+2. **Perturb Initial Grid:** I will add a small amount of random noise to the initial 25-circle grid. This is a critical step to break the initial symmetry, preventing forces from canceling each other out and allowing the simulation to explore a much wider range of configurations.
+3. **Robust Radius Convergence:** I will improve the `compute_max_radii` function by implementing a more robust convergence criterion based on the magnitude of radius changes. This ensures the forces used in the simulation are calculated from more accurate radii, leading to a higher-quality final packing.
+
+These combined changes are designed to significantly enhance the exploratory power of the simulation, enabling it to break free from the initial grid and discover denser, more optimal packings, thereby increasing the sum of radii.
+
+
+increase_simulation_intensity
+
+
+The current simulation hyperparameters (iteration counts, growth pressure) are less aggressive than those in prior, better-performing versions. The low score and grid-like final arrangement suggest the simulation isn't sufficiently "melting" the initial grid to find a denser packing. By increasing `sim_iter` to 2500, `fine_tune_iter` to 600, `radius_sim_iter` to 150, and `initial_growth_pressure` to 1.03, we give the system more time and energy to escape the local optimum of the grid configuration. These values are borrowed from a previous version that achieved a score of 1.98.
+
+
+<<<<<<< SEARCH
+ # --- Hyperparameters for the simulation phases ---
+ sim_iter = 2000
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.02
+ final_growth_pressure = 1.001
+ fine_tune_iter = 400
+ fine_tune_lr = 0.001
+ radius_sim_iter = 75 # Iterations for compute_max_radii during simulation (for speed)
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+=======
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+>>>>>>> REPLACE
+
+
+perturb_initial_grid
+
+
+The current simulation starts from a perfect 5x5 grid. This high degree of symmetry can cause forces to cancel out, trapping the simulation in a local optimum close to the initial state, as seen in the visualization. To overcome this, I am introducing a small random perturbation to the initial positions of the 25 grid circles. This breaks the symmetry, allowing the force-directed simulation to more effectively explore non-grid-like, potentially denser packing arrangements. A small perturbation (`0.01 * spacing`) is sufficient to kickstart this process without losing the benefit of a good initial guess.
+
+
+<<<<<<< SEARCH
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ # Create a new 'centers' array for this trial to avoid modifying base_25_centers
+ current_centers_trial = np.zeros((n, 2))
+ current_centers_trial[:n-1] = base_25_centers.copy()
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+=======
+ # Add a small perturbation to the base grid to break symmetry and encourage rearrangement.
+ perturbation_strength = 0.01 * spacing # 1% of the grid spacing
+
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+
+ # Start with a fresh copy of the base grid and perturb it
+ perturbed_grid = base_25_centers.copy()
+ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+>>>>>>> REPLACE
+
+
+robust_radius_convergence
+
+
+The `compute_max_radii` function's termination condition (`if not updated_in_pass`) can be fragile. It might exit prematurely if one pass makes no changes, even if the radii are not fully converged. I am replacing this with a more robust check that terminates only when both no updates are made AND the maximum change in any radius between iterations is below a small epsilon (`1e-8`). This ensures a more accurate radius calculation, which is critical for both the force calculations during the simulation and the final fitness evaluation. This change also incorporates a safer check for co-located centers from a prior successful implementation.
+
+
+<<<<<<< SEARCH
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # The number of iterations is parameterized for performance tuning.
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # This case should not happen with this constructor, but it's a safe guard.
+ # If centers are identical, their radii must be zero.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized.
+ if not updated_in_pass:
+ break
+=======
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b560489e8c0ba07f4a8d83f5099c08017b7784b4
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..3daa358b3bef510f02bcbcc2222e6c9813d2db47
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/edit.diff
@@ -0,0 +1,334 @@
+--- a/original.py
++++ b/original.py
+@@ -1,309 +1,288 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+- # Tuned based on high-scoring prior programs.
+- simulation_iterations = 1000 # Increased iterations for thorough refinement.
++ # Tuned for more aggressive exploration based on high-scoring programs.
++ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+- initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
++ initial_growth_pressure = 1.04 # Stronger initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+- fine_tune_iter = 200 # Increased iterations for final settlement.
++ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
++
++ # Add a small random perturbation to the base grid to break symmetry
++ perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
++ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
++
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+- centers_for_sim = current_centers.copy() # Operate on a copy for simulation
++ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b. Wall repulsion forces (based on pressured radii)
+- for i in range(n):
+- # Left wall
+- overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
++ # b. Wall repulsion forces (vectorized, based on pressured radii)
++ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
++ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
++ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
++ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b. Wall repulsion forces (based on actual radii)
+- for i in range(n):
+- # Left wall
+- overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- # Right wall
+- overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- # Bottom wall
+- overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- # Top wall
+- overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
++ # b. Wall repulsion forces (vectorized, based on actual radii)
++ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
++ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
++ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
++ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+ def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+- for _ in range(250):
++ # Fewer iterations for performance; high accuracy is less critical during simulation.
++ for _ in range(150):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..18c11fe7ebc12532546cc96943b9d5af39e00a40
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/main.py
@@ -0,0 +1,288 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration based on high-scoring programs.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # Stronger initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ for _ in range(150):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fc8c7dbfd5b6e3983eb39b62837085607d66e9d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/original.py
@@ -0,0 +1,309 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 1000 # Increased iterations for thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 200 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (based on actual radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..9c3d7c8759d6a17433aa7938713dec9050ab81c6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results
+Run 1/1 completed in 77.55 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9794465064160045
+ public: {'centers_str': ' centers[0] = (0.0746, 0.0746)\n centers[1] = (0.3258, 0.0740)\n centers[2] = (0.5016, 0.0987)\n centers[3] = (0.7012, 0.1000)\n centers[4] = (0.8976, 0.1022)\n centers[5] = (0.0719, 0.3301)\n centers[6] = (0.3212, 0.3229)\n centers[7] = (0.5013, 0.2997)\n centers[8] = (0.6999, 0.3009)\n centers[9] = (0.9003, 0.3008)\n centers[10] = (0.0999, 0.5005)\n centers[11] = (0.2994, 0.5010)\n centers[12] = (0.4994, 0.4996)\n centers[13] = (0.6989, 0.4680)\n centers[14] = (0.8973, 0.5001)\n centers[15] = (0.0971, 0.6995)\n centers[16] = (0.2677, 0.6843)\n centers[17] = (0.4559, 0.6834)\n centers[18] = (0.7155, 0.6868)\n centers[19] = (0.9346, 0.7019)\n centers[20] = (0.0879, 0.9012)\n centers[21] = (0.2927, 0.8829)\n centers[22] = (0.5204, 0.8900)\n centers[23] = (0.7090, 0.9205)\n centers[24] = (0.8938, 0.8936)\n centers[25] = (0.2072, 0.1982)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9794465064160045}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/packing_viz.png
+ execution_time_mean: 77.54707105597481
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..2e84f3a500f5d8ccfd64dfeedc613edaf5d1dfee
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9794465064160045,
+ "public": {
+ "centers_str": " centers[0] = (0.0746, 0.0746)\n centers[1] = (0.3258, 0.0740)\n centers[2] = (0.5016, 0.0987)\n centers[3] = (0.7012, 0.1000)\n centers[4] = (0.8976, 0.1022)\n centers[5] = (0.0719, 0.3301)\n centers[6] = (0.3212, 0.3229)\n centers[7] = (0.5013, 0.2997)\n centers[8] = (0.6999, 0.3009)\n centers[9] = (0.9003, 0.3008)\n centers[10] = (0.0999, 0.5005)\n centers[11] = (0.2994, 0.5010)\n centers[12] = (0.4994, 0.4996)\n centers[13] = (0.6989, 0.4680)\n centers[14] = (0.8973, 0.5001)\n centers[15] = (0.0971, 0.6995)\n centers[16] = (0.2677, 0.6843)\n centers[17] = (0.4559, 0.6834)\n centers[18] = (0.7155, 0.6868)\n centers[19] = (0.9346, 0.7019)\n centers[20] = (0.0879, 0.9012)\n centers[21] = (0.2927, 0.8829)\n centers[22] = (0.5204, 0.8900)\n centers[23] = (0.7090, 0.9205)\n centers[24] = (0.8938, 0.8936)\n centers[25] = (0.2072, 0.1982)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9794465064160045
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/results/packing_viz.png",
+ "execution_time_mean": 77.54707105597481,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8a776dfc2bc820ef2df95bf4e3edf5491362437c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_72/search_replace.txt
@@ -0,0 +1,121 @@
+My analysis shows the current simulation is too gentle, failing to break the strong symmetry of the initial 5x5 grid, as seen in the visualization. This traps the solution in a sub-optimal, grid-like configuration. High-scoring prior programs used a more aggressive "explosive" start, combined with performance optimizations, to explore a wider variety of more irregular, and thus potentially denser, packings.
+
+My proposed edit implements a unified strategy to overhaul the simulation. It introduces an initial random perturbation to break symmetry, vectorizes the computationally expensive wall-force calculations for a massive speedup, and then uses this newfound performance to fund a much longer and more aggressive simulation with a higher initial growth pressure. This allows the system to escape the initial grid's local minimum and explore the solution space more thoroughly.
+
+
+aggressive_perturbed_simulation
+
+
+This edit overhauls the simulation strategy to break the initial grid symmetry and enable a more thorough search. It's a three-part approach inspired by high-scoring prior programs:
+
+1. **Symmetry Breaking & Aggressive Tuning:** Introduces a small random perturbation to the initial 5x5 grid to prevent the simulation from getting stuck in a rigid, grid-like local optimum. It also increases the simulation budget (`simulation_iterations` to 2000, `fine_tune_iter` to 400) and the `initial_growth_pressure` to 1.04, creating a more "explosive" start that forces the circles to rearrange more dynamically.
+
+2. **Performance via Vectorization:** Replaces the slow, looped calculations for wall repulsion forces with highly efficient, vectorized NumPy operations in both the main simulation and fine-tuning phases. This critical optimization makes the increased simulation budget feasible.
+
+3. **Simulation Speed-up:** The number of iterations in `compute_radii_for_simulation` is reduced from 250 to 150. Perfect radius accuracy is unnecessary during the intermediate simulation steps, and this change further accelerates the process, allowing more iterations to be completed in the same time frame.
+
+
+<<<<<<< SEARCH
+ # Tuned based on high-scoring prior programs.
+ simulation_iterations = 1000 # Increased iterations for thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.02 # Start with stronger pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 200 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+=======
+ # Tuned for more aggressive exploration based on high-scoring programs.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # Stronger initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+>>>>>>> REPLACE
+<<<<<<< SEARCH
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+=======
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+>>>>>>> REPLACE
+<<<<<<< SEARCH
+ # b. Wall repulsion forces (based on actual radii)
+ for i in range(n):
+ # Left wall
+ overlap_left = radii_for_sim[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ # Right wall
+ overlap_right = (centers_for_sim[i, 0] + radii_for_sim[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ # Bottom wall
+ overlap_bottom = radii_for_sim[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ # Top wall
+ overlap_top = (centers_for_sim[i, 1] + radii_for_sim[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+=======
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+>>>>>>> REPLACE
+<<<<<<< SEARCH
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+=======
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ for _ in range(150):
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6c25e5ccd9c1c25361383ec58cfd0dcaaab9d4f0
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..0e03e1e0cdf5ba8cb1ff4d75105a69c808df9c17
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/edit.diff
@@ -0,0 +1,561 @@
+--- a/original.py
++++ b/original.py
+@@ -1,304 +1,280 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
++"""
++Physical simulation-based circle packing for n=26 circles.
++This algorithm uses a multi-start strategy with an "Adaptive Composite Initialization"
++combined with a fully vectorized, aggressive-then-conservative force-directed simulation
++with advanced annealing schedules.
++"""
+
+ import numpy as np
+
+-
+-def construct_packing():
+- """
+- Construct a specific arrangement of 26 circles in a unit square
+- that attempts to maximize the sum of their radii.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- # Initialize arrays for 26 circles
+- n = 26
+-
+- # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side # spacing is 0.2
+-
+- base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+- k = 0
+- for j in range(num_cells_side):
+- for i in range(num_cells_side):
+- base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+- base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+- k += 1
+-
+- # Define strategic candidate placements for the 26th circle.
+- # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+- candidate_extra_positions = [
+- # Interstitial points from 5x5 grid
+- [spacing, spacing], # (0.2, 0.2)
+- [spacing, 0.5], # (0.2, 0.5)
+- [spacing, 1 - spacing], # (0.2, 0.8)
+- [0.5, spacing], # (0.5, 0.2)
+- [0.5, 0.5], # (0.5, 0.5) - Center
+- [0.5, 1 - spacing], # (0.5, 0.8)
+- [1 - spacing, spacing], # (0.8, 0.2)
+- [1 - spacing, 0.5], # (0.8, 0.5)
+- [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- # Corner-offset points
+- [0.05, 0.05],
+- [0.05, 0.95],
+- [0.95, 0.05],
+- [0.95, 0.95]
+- ]
+-
+- best_sum_radii = 0
+- best_centers = None
+- best_radii = None
+-
+- # --- Simulation Hyperparameters ---
+- # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+- main_iterations = 1200 # Number of steps for main force-directed simulation
+- main_learning_rate = 0.01 # Initial learning rate for main phase
+- wall_repulsion_strength = 0.35 # Balanced wall repulsion to utilize boundaries effectively
+- initial_growth_pressure = 1.018 # Stronger initial growth pressure for aggressive exploration
+- final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+-
+- fine_tune_iterations = 300 # Number of steps for fine-tuning
+- fine_tune_learning_rate = 0.0008 # Very low learning rate for precise adjustments
+- finetune_growth_pressure_start = 1.0005 # Subtle growth pressure for fine-tuning
+- finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+-
+- # Iterate through candidate positions for the 26th circle to find the best one.
+- for extra_pos in candidate_extra_positions:
+- current_centers = np.zeros((n, 2))
+- current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+- current_centers[25] = np.array(extra_pos)
+-
+- # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+- centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+-
+- for sim_iter in range(main_iterations):
+- radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+-
+- # Anneal growth pressure quadratically
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * \
+- (1.0 - (sim_iter / main_iterations))**2
+- pressured_radii = radii_for_sim * current_growth_pressure
+-
+- forces = np.zeros((n, 2))
+-
+- # a. Circle-to-circle repulsion forces (based on pressured radii)
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers_for_sim[i] - centers_for_sim[j]
+- dist = np.linalg.norm(vec)
+-
+- if dist < 1e-9: # Centers are too close.
+- continue
+-
+- overlap = pressured_radii[i] + pressured_radii[j] - dist
+-
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces[i] += force_vec
+- forces[j] -= force_vec # Equal and opposite reaction
+-
+- # b. Wall repulsion forces (based on pressured radii)
+- for i in range(n):
+- overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces[i, 0] += wall_repulsion_strength * overlap_left
+- overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+- if overlap_right > 0:
+- forces[i, 0] -= wall_repulsion_strength * overlap_right
+- overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces[i, 1] += wall_repulsion_strength * overlap_bottom
+- overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+- if overlap_top > 0:
+- forces[i, 1] -= wall_repulsion_strength * overlap_top
+-
+- # 3. Update center positions based on forces
+- # Annealing learning rate: starts high, decreases over time
+- current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+- centers_for_sim += forces * current_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # --- Post-Simulation Fine-Tuning Phase ---
+- # Continue from the best state of the main simulation for precision.
+- for fine_tune_iter in range(fine_tune_iterations):
+- radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+-
+- # Anneal subtle growth pressure (linear decay)
+- current_finetune_growth_pressure = finetune_growth_pressure_start - \
+- (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+- (fine_tune_iter / fine_tune_iterations)
+- pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+-
+- forces_ft = np.zeros((n, 2))
+-
+- # a. Circle-to-circle repulsion forces
+- for i in range(n):
+- for j in range(i + 1, n):
+- vec = centers_for_sim[i] - centers_for_sim[j]
+- dist = np.linalg.norm(vec)
+-
+- if dist < 1e-9:
+- continue
+-
+- overlap = pressured_radii_ft[i] + pressured_radii_ft[j] - dist
+-
+- if overlap > 0:
+- force_magnitude = overlap
+- force_vec = (vec / dist) * force_magnitude
+- forces_ft[i] += force_vec
+- forces_ft[j] -= force_vec
+-
+- # b. Wall repulsion forces
+- for i in range(n):
+- overlap_left = pressured_radii_ft[i] - centers_for_sim[i, 0]
+- if overlap_left > 0:
+- forces_ft[i, 0] += wall_repulsion_strength * overlap_left
+- overlap_right = (centers_for_sim[i, 0] + pressured_radii_ft[i]) - 1.0
+- if overlap_right > 0:
+- forces_ft[i, 0] -= wall_repulsion_strength * overlap_right
+- overlap_bottom = pressured_radii_ft[i] - centers_for_sim[i, 1]
+- if overlap_bottom > 0:
+- forces_ft[i, 1] += wall_repulsion_strength * overlap_bottom
+- overlap_top = (centers_for_sim[i, 1] + pressured_radii_ft[i]) - 1.0
+- if overlap_top > 0:
+- forces_ft[i, 1] -= wall_repulsion_strength * overlap_top
+-
+- # Update center positions
+- current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+- centers_for_sim += forces_ft * current_fine_tune_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+- current_radii = compute_max_radii(centers_for_sim)
+- current_sum_radii = np.sum(current_radii)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers = centers_for_sim.copy() # Store the optimized centers
+- best_radii = current_radii.copy()
+-
+- # Fallback if no valid configuration was found (should not happen with good candidates)
+- if best_centers is None:
+- # This fallback uses the base 5x5 + center as a safe default
+- final_centers = np.zeros((n, 2))
+- final_centers[:25] = base_centers
+- final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+- final_radii = compute_max_radii(final_centers)
+- return final_centers, final_radii
+- else:
+- return best_centers, best_radii
+-
+-
+-def compute_max_radii(centers):
++def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+- This function performs a full, highly convergent iteration.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
++ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence for final radii
+- for _ in range(500):
+- updated_in_pass = False
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ for _ in range(max_iter):
++ old_radii = radii.copy()
++ updated = False
++
++ # Iterative update for proportional scaling, proven robust
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+-
+- # If current radii would cause overlap and distance is not zero
+- if dist < 1e-9: # Handle cases where centers might be identical or very close
+- # If centers are practically the same, they cannot both have positive radius.
+- # For packing, it's safer to consider them as overlapping severely and shrink them.
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
+- continue
+-
+ if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- # Scale both radii proportionally to just touch
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- # If a full pass over all pairs results in no change, radii have stabilized
+- if not updated_in_pass:
++ if dist < 1e-9: # Centers are practically identical
++ radii[i], radii[j] = 0.0, 0.0
++ else:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++
++ if not updated or (updated and np.max(np.abs(radii - old_radii)) < convergence_threshold):
+ break
+-
++
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+-
+-def compute_radii_for_simulation(centers):
+- """
+- Compute approximate maximum radii for each circle position
+- for use inside the simulation loop. Fewer iterations for performance.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+- for _ in range(250):
+- updated_in_pass = False
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9:
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0)
+-
+-
++def initialize_centers_strategy(n, strategy_type, perturbation_scale):
++ """
++ Generates initial center positions based on a chosen strategy.
++ """
++ centers = np.zeros((n, 2))
++
++ if strategy_type == 'random_scatter':
++ # All circles are randomly placed, then undergo a light pre-dispersion FDS
++ centers = np.random.rand(n, 2)
++
++ # Apply a very short, aggressive repulsion to spread them out
++ temp_lr = 0.015 # Higher temp LR
++ temp_growth = 1.05 # Stronger temp growth
++ temp_wall_strength = 0.6 # Stronger temp walls
++ temp_iterations = 75 # More iterations for better initial spread
++
++ for _ in range(temp_iterations):
++ temp_radii = compute_max_radii(centers, max_iter=75, convergence_threshold=1e-4) # Fast radii for temp sim
++ inflated_temp_radii = temp_radii * temp_growth
++
++ # Vectorized Circle-to-circle repulsion
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ dists[dists < 1e-9] = 1e-9 # Prevent div by zero for identical centers
++
++ sum_inflated_radii = inflated_temp_radii[:, np.newaxis] + inflated_temp_radii[np.newaxis, :]
++ overlaps = np.maximum(0, sum_inflated_radii - dists)
++ np.fill_diagonal(overlaps, 0) # No self-repulsion
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / dists[:, :, np.newaxis]
++ unit_vectors[~np.isfinite(unit_vectors)] = 0 # Forces to zero for identical centers
++
++ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
++
++ # Vectorized Wall repulsion forces
++ wall_forces = np.zeros_like(centers)
++ overlap_left = inflated_temp_radii - centers[:, 0]
++ overlap_right = (centers[:, 0] + inflated_temp_radii) - 1.0
++ overlap_bottom = inflated_temp_radii - centers[:, 1]
++ overlap_top = (centers[:, 1] + inflated_temp_radii) - 1.0
++
++ wall_forces[:, 0] += temp_wall_strength * np.maximum(0, overlap_left)
++ wall_forces[:, 0] -= temp_wall_strength * np.maximum(0, overlap_right)
++ wall_forces[:, 1] += temp_wall_strength * np.maximum(0, overlap_bottom)
++ wall_forces[:, 1] -= temp_wall_strength * np.maximum(0, overlap_top)
++
++ centers += (circle_forces + wall_forces) * temp_lr
++ else: # 'central_void' or 'corner_void' strategies
++ # Calculate number of cells for a grid (sqrt(n-1))
++ # For n=26, n-1=25, so sqrt(25)=5
++ num_grid_circles = n - 1 # Most circles will be placed on a grid
++ num_cells_side = int(np.ceil(np.sqrt(num_grid_circles)))
++ spacing = 1.0 / num_cells_side
++
++ k = 0
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ if k < num_grid_circles:
++ centers[k, 0] = (i + 0.5) * spacing
++ centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ # Perturb the base circles
++ centers[:num_grid_circles, :] += np.random.normal(0, spacing * perturbation_scale, size=(num_grid_circles, 2))
++
++ # Place the last circle based on strategy
++ if strategy_type == 'central_void':
++ centers[n-1] = [0.5, 0.5] + np.random.normal(0, spacing * perturbation_scale, size=2)
++ elif strategy_type == 'corner_void':
++ corners = [[0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95]]
++ chosen_corner = corners[np.random.randint(len(corners))]
++ centers[n-1] = chosen_corner + np.random.normal(0, spacing * perturbation_scale * 0.5, size=2)
++
++ return np.clip(centers, 0.0, 1.0)
++
++
++def construct_packing():
++ """
++ Constructs a packing of 26 circles by simulating physical repulsion forces
++ to find an optimal arrangement of centers. Uses Adaptive Composite Initialization
++ and a two-phase, fully vectorized force-directed simulation.
++ """
++ n = 26
++
++ # --- Simulation Hyperparameters ---
++ # Main Phase: Aggressive Exploration
++ main_iterations = 2500
++ main_learning_rate_start = 0.025
++ main_learning_rate_end = 0.001
++ initial_growth_pressure = 1.04
++ final_growth_pressure = 1.0001
++ wall_repulsion_strength_main = 0.4
++
++ # Fine-tuning Phase: Conservative Exploitation
++ fine_tune_iterations = 700
++ fine_tune_learning_rate_start = 0.001
++ fine_tune_learning_rate_end = 0.0001
++ finetune_growth_pressure_start = 1.0005
++ finetune_growth_pressure_end = 1.000001
++ wall_repulsion_strength_fine_tune = 0.3
++
++ # Multi-start parameters
++ num_trials = 12 # Increased trials for better global search
++
++ best_sum_radii = -1.0
++ best_final_centers = None
++
++ # Define initialization strategies
++ init_strategies = ['central_void', 'corner_void', 'random_scatter']
++
++ for trial in range(num_trials):
++ # Adaptive Composite Initialization: Randomly choose a strategy
++ chosen_strategy = np.random.choice(init_strategies)
++ current_centers = initialize_centers_strategy(n, chosen_strategy, perturbation_scale=0.012) # Slightly increased perturbation
++
++ centers_for_sim = current_centers.copy()
++
++ # --- Main Physical Simulation Loop (Vectorized) ---
++ for sim_iter in range(main_iterations):
++ # Anneal learning rate (cubic decay for aggressive start, faster reduction)
++ current_lr_main = main_learning_rate_end + \
++ (main_learning_rate_start - main_learning_rate_end) * \
++ (1.0 - (sim_iter / main_iterations))**3
++
++ # Anneal growth pressure (cubic decay for aggressive start, faster reduction)
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * \
++ (1.0 - (sim_iter / main_iterations))**3
++
++ radii_for_sim = compute_max_radii(centers_for_sim, max_iter=150, convergence_threshold=1e-4) # Faster radii calc for simulation
++ pressured_radii = radii_for_sim * current_growth_pressure
++
++ # --- Vectorized Circle-to-circle repulsion forces ---
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ dists[dists < 1e-9] = 1e-9 # Prevent div by zero for identical centers
++
++ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = np.maximum(0, sum_pressured_radii - dists)
++ np.fill_diagonal(overlaps, 0) # No self-repulsion
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / dists[:, :, np.newaxis]
++ unit_vectors[~np.isfinite(unit_vectors)] = 0 # Forces to zero for identical centers
++
++ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
++
++ # --- Vectorized Wall repulsion forces ---
++ wall_forces = np.zeros_like(centers_for_sim)
++ overlap_left = pressured_radii - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
++ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
++
++ wall_forces[:, 0] += wall_repulsion_strength_main * np.maximum(0, overlap_left)
++ wall_forces[:, 0] -= wall_repulsion_strength_main * np.maximum(0, overlap_right)
++ wall_forces[:, 1] += wall_repulsion_strength_main * np.maximum(0, overlap_bottom)
++ wall_forces[:, 1] -= wall_repulsion_strength_main * np.maximum(0, overlap_top)
++
++ # Update center positions
++ centers_for_sim += (circle_forces + wall_forces) * current_lr_main
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
++ # Continue from the best state of the main simulation for precision.
++ for fine_tune_iter in range(fine_tune_iterations):
++ # Anneal learning rate (linear decay for stability)
++ current_lr_fine_tune = fine_tune_learning_rate_end + \
++ (fine_tune_learning_rate_start - fine_tune_learning_rate_end) * \
++ (1.0 - (fine_tune_iter / fine_tune_iterations))
++
++ # Anneal subtle growth pressure (linear decay)
++ current_finetune_growth_pressure = finetune_growth_pressure_end + \
++ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
++ (1.0 - (fine_tune_iter / fine_tune_iterations))
++
++ radii_for_sim = compute_max_radii(centers_for_sim, max_iter=150, convergence_threshold=1e-4) # Faster radii calc
++ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
++
++ # --- Vectorized Circle-to-circle repulsion forces ---
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ dists[dists < 1e-9] = 1e-9 # Prevent div by zero
++
++ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
++ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / dists[:, :, np.newaxis]
++ unit_vectors[~np.isfinite(unit_vectors)] = 0
++
++ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
++
++ # --- Vectorized Wall repulsion forces ---
++ wall_forces = np.zeros_like(centers_for_sim)
++ overlap_left = pressured_radii_ft - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
++ overlap_bottom = pressured_radii_ft - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
++
++ wall_forces[:, 0] += wall_repulsion_strength_fine_tune * np.maximum(0, overlap_left)
++ wall_forces[:, 0] -= wall_repulsion_strength_fine_tune * np.maximum(0, overlap_right)
++ wall_forces[:, 1] += wall_repulsion_strength_fine_tune * np.maximum(0, overlap_bottom)
++ wall_forces[:, 1] -= wall_repulsion_strength_fine_tune * np.maximum(0, overlap_top)
++
++ # Update center positions
++ centers_for_sim += (circle_forces + wall_forces) * current_lr_fine_tune
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
++ current_radii = compute_max_radii(centers_for_sim, max_iter=1000) # High precision final radii
++ current_sum_radii = np.sum(current_radii)
++
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_final_centers = centers_for_sim.copy()
++
++ # Final check: if no suitable solution found, default to a basic one.
++ if best_final_centers is None:
++ best_final_centers = initialize_centers_strategy(n, 'central_void', perturbation_scale=0.01) # A fallback
++
++ final_radii = compute_max_radii(best_final_centers, max_iter=1500) # Very high precision for final output
++
++ return best_final_centers, final_radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ac699055fe081878bf61753ff68909e46ac99e8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/main.py
@@ -0,0 +1,280 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm uses a multi-start strategy with an "Adaptive Composite Initialization"
+combined with a fully vectorized, aggressive-then-conservative force-directed simulation
+with advanced annealing schedules.
+"""
+
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+
+ # Iterative update for proportional scaling, proven robust
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Centers are practically identical
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or (updated and np.max(np.abs(radii - old_radii)) < convergence_threshold):
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+def initialize_centers_strategy(n, strategy_type, perturbation_scale):
+ """
+ Generates initial center positions based on a chosen strategy.
+ """
+ centers = np.zeros((n, 2))
+
+ if strategy_type == 'random_scatter':
+ # All circles are randomly placed, then undergo a light pre-dispersion FDS
+ centers = np.random.rand(n, 2)
+
+ # Apply a very short, aggressive repulsion to spread them out
+ temp_lr = 0.015 # Higher temp LR
+ temp_growth = 1.05 # Stronger temp growth
+ temp_wall_strength = 0.6 # Stronger temp walls
+ temp_iterations = 75 # More iterations for better initial spread
+
+ for _ in range(temp_iterations):
+ temp_radii = compute_max_radii(centers, max_iter=75, convergence_threshold=1e-4) # Fast radii for temp sim
+ inflated_temp_radii = temp_radii * temp_growth
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent div by zero for identical centers
+
+ sum_inflated_radii = inflated_temp_radii[:, np.newaxis] + inflated_temp_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / dists[:, :, np.newaxis]
+ unit_vectors[~np.isfinite(unit_vectors)] = 0 # Forces to zero for identical centers
+
+ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
+
+ # Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers)
+ overlap_left = inflated_temp_radii - centers[:, 0]
+ overlap_right = (centers[:, 0] + inflated_temp_radii) - 1.0
+ overlap_bottom = inflated_temp_radii - centers[:, 1]
+ overlap_top = (centers[:, 1] + inflated_temp_radii) - 1.0
+
+ wall_forces[:, 0] += temp_wall_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= temp_wall_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += temp_wall_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= temp_wall_strength * np.maximum(0, overlap_top)
+
+ centers += (circle_forces + wall_forces) * temp_lr
+ else: # 'central_void' or 'corner_void' strategies
+ # Calculate number of cells for a grid (sqrt(n-1))
+ # For n=26, n-1=25, so sqrt(25)=5
+ num_grid_circles = n - 1 # Most circles will be placed on a grid
+ num_cells_side = int(np.ceil(np.sqrt(num_grid_circles)))
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < num_grid_circles:
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Perturb the base circles
+ centers[:num_grid_circles, :] += np.random.normal(0, spacing * perturbation_scale, size=(num_grid_circles, 2))
+
+ # Place the last circle based on strategy
+ if strategy_type == 'central_void':
+ centers[n-1] = [0.5, 0.5] + np.random.normal(0, spacing * perturbation_scale, size=2)
+ elif strategy_type == 'corner_void':
+ corners = [[0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95]]
+ chosen_corner = corners[np.random.randint(len(corners))]
+ centers[n-1] = chosen_corner + np.random.normal(0, spacing * perturbation_scale * 0.5, size=2)
+
+ return np.clip(centers, 0.0, 1.0)
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers. Uses Adaptive Composite Initialization
+ and a two-phase, fully vectorized force-directed simulation.
+ """
+ n = 26
+
+ # --- Simulation Hyperparameters ---
+ # Main Phase: Aggressive Exploration
+ main_iterations = 2500
+ main_learning_rate_start = 0.025
+ main_learning_rate_end = 0.001
+ initial_growth_pressure = 1.04
+ final_growth_pressure = 1.0001
+ wall_repulsion_strength_main = 0.4
+
+ # Fine-tuning Phase: Conservative Exploitation
+ fine_tune_iterations = 700
+ fine_tune_learning_rate_start = 0.001
+ fine_tune_learning_rate_end = 0.0001
+ finetune_growth_pressure_start = 1.0005
+ finetune_growth_pressure_end = 1.000001
+ wall_repulsion_strength_fine_tune = 0.3
+
+ # Multi-start parameters
+ num_trials = 12 # Increased trials for better global search
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Define initialization strategies
+ init_strategies = ['central_void', 'corner_void', 'random_scatter']
+
+ for trial in range(num_trials):
+ # Adaptive Composite Initialization: Randomly choose a strategy
+ chosen_strategy = np.random.choice(init_strategies)
+ current_centers = initialize_centers_strategy(n, chosen_strategy, perturbation_scale=0.012) # Slightly increased perturbation
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Physical Simulation Loop (Vectorized) ---
+ for sim_iter in range(main_iterations):
+ # Anneal learning rate (cubic decay for aggressive start, faster reduction)
+ current_lr_main = main_learning_rate_end + \
+ (main_learning_rate_start - main_learning_rate_end) * \
+ (1.0 - (sim_iter / main_iterations))**3
+
+ # Anneal growth pressure (cubic decay for aggressive start, faster reduction)
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**3
+
+ radii_for_sim = compute_max_radii(centers_for_sim, max_iter=150, convergence_threshold=1e-4) # Faster radii calc for simulation
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # --- Vectorized Circle-to-circle repulsion forces ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent div by zero for identical centers
+
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / dists[:, :, np.newaxis]
+ unit_vectors[~np.isfinite(unit_vectors)] = 0 # Forces to zero for identical centers
+
+ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
+
+ # --- Vectorized Wall repulsion forces ---
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength_main * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength_main * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength_main * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength_main * np.maximum(0, overlap_top)
+
+ # Update center positions
+ centers_for_sim += (circle_forces + wall_forces) * current_lr_main
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ # Anneal learning rate (linear decay for stability)
+ current_lr_fine_tune = fine_tune_learning_rate_end + \
+ (fine_tune_learning_rate_start - fine_tune_learning_rate_end) * \
+ (1.0 - (fine_tune_iter / fine_tune_iterations))
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_end + \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (1.0 - (fine_tune_iter / fine_tune_iterations))
+
+ radii_for_sim = compute_max_radii(centers_for_sim, max_iter=150, convergence_threshold=1e-4) # Faster radii calc
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ # --- Vectorized Circle-to-circle repulsion forces ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent div by zero
+
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / dists[:, :, np.newaxis]
+ unit_vectors[~np.isfinite(unit_vectors)] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
+
+ # --- Vectorized Wall repulsion forces ---
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength_fine_tune * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength_fine_tune * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength_fine_tune * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength_fine_tune * np.maximum(0, overlap_top)
+
+ # Update center positions
+ centers_for_sim += (circle_forces + wall_forces) * current_lr_fine_tune
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim, max_iter=1000) # High precision final radii
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # Final check: if no suitable solution found, default to a basic one.
+ if best_final_centers is None:
+ best_final_centers = initialize_centers_strategy(n, 'central_void', perturbation_scale=0.01) # A fallback
+
+ final_radii = compute_max_radii(best_final_centers, max_iter=1500) # Very high precision for final output
+
+ return best_final_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..392c1830f2d91ecbf79feef1962c630f2f64903c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/original.py
@@ -0,0 +1,304 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+ main_iterations = 1200 # Number of steps for main force-directed simulation
+ main_learning_rate = 0.01 # Initial learning rate for main phase
+ wall_repulsion_strength = 0.35 # Balanced wall repulsion to utilize boundaries effectively
+ initial_growth_pressure = 1.018 # Stronger initial growth pressure for aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+ fine_tune_iterations = 300 # Number of steps for fine-tuning
+ fine_tune_learning_rate = 0.0008 # Very low learning rate for precise adjustments
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure for fine-tuning
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces (based on pressured radii)
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # Centers are too close.
+ continue
+
+ overlap = pressured_radii[i] + pressured_radii[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec # Equal and opposite reaction
+
+ # b. Wall repulsion forces (based on pressured radii)
+ for i in range(n):
+ overlap_left = pressured_radii[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap_left
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii[i]) - 1.0
+ if overlap_right > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap_right
+ overlap_bottom = pressured_radii[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap_bottom
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii[i]) - 1.0
+ if overlap_top > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers_for_sim[i] - centers_for_sim[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ continue
+
+ overlap = pressured_radii_ft[i] + pressured_radii_ft[j] - dist
+
+ if overlap > 0:
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces_ft[i] += force_vec
+ forces_ft[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ overlap_left = pressured_radii_ft[i] - centers_for_sim[i, 0]
+ if overlap_left > 0:
+ forces_ft[i, 0] += wall_repulsion_strength * overlap_left
+ overlap_right = (centers_for_sim[i, 0] + pressured_radii_ft[i]) - 1.0
+ if overlap_right > 0:
+ forces_ft[i, 0] -= wall_repulsion_strength * overlap_right
+ overlap_bottom = pressured_radii_ft[i] - centers_for_sim[i, 1]
+ if overlap_bottom > 0:
+ forces_ft[i, 1] += wall_repulsion_strength * overlap_bottom
+ overlap_top = (centers_for_sim[i, 1] + pressured_radii_ft[i]) - 1.0
+ if overlap_top > 0:
+ forces_ft[i, 1] -= wall_repulsion_strength * overlap_top
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, as in high-scoring runs.
+ for _ in range(250):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..d632077819aeadc3625265903d132e1aeae02b6d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results
+Run 1/1 completed in 88.71 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.917558668809064
+ public: {'centers_str': ' centers[0] = (0.1148, 0.1106)\n centers[1] = (0.3015, 0.1027)\n centers[2] = (0.4987, 0.1008)\n centers[3] = (0.6988, 0.1038)\n centers[4] = (0.8982, 0.1014)\n centers[5] = (0.1011, 0.2973)\n centers[6] = (0.2944, 0.2978)\n centers[7] = (0.4966, 0.2982)\n centers[8] = (0.7020, 0.3009)\n centers[9] = (0.9005, 0.2984)\n centers[10] = (0.1017, 0.4992)\n centers[11] = (0.2990, 0.4965)\n centers[12] = (0.4992, 0.5017)\n centers[13] = (0.7013, 0.4603)\n centers[14] = (0.8998, 0.4930)\n centers[15] = (0.0978, 0.7008)\n centers[16] = (0.2651, 0.6999)\n centers[17] = (0.4564, 0.6937)\n centers[18] = (0.7264, 0.6889)\n centers[19] = (0.9436, 0.7030)\n centers[20] = (0.0949, 0.8962)\n centers[21] = (0.2999, 0.8903)\n centers[22] = (0.5123, 0.8978)\n centers[23] = (0.7005, 0.9245)\n centers[24] = (0.8949, 0.8951)\n centers[25] = (0.0409, 0.0409)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.917558668809064}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/packing_viz.png
+ execution_time_mean: 88.71411704784259
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..6672cea41cc8df07bf304e731e601e9039b99e56
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.917558668809064,
+ "public": {
+ "centers_str": " centers[0] = (0.1148, 0.1106)\n centers[1] = (0.3015, 0.1027)\n centers[2] = (0.4987, 0.1008)\n centers[3] = (0.6988, 0.1038)\n centers[4] = (0.8982, 0.1014)\n centers[5] = (0.1011, 0.2973)\n centers[6] = (0.2944, 0.2978)\n centers[7] = (0.4966, 0.2982)\n centers[8] = (0.7020, 0.3009)\n centers[9] = (0.9005, 0.2984)\n centers[10] = (0.1017, 0.4992)\n centers[11] = (0.2990, 0.4965)\n centers[12] = (0.4992, 0.5017)\n centers[13] = (0.7013, 0.4603)\n centers[14] = (0.8998, 0.4930)\n centers[15] = (0.0978, 0.7008)\n centers[16] = (0.2651, 0.6999)\n centers[17] = (0.4564, 0.6937)\n centers[18] = (0.7264, 0.6889)\n centers[19] = (0.9436, 0.7030)\n centers[20] = (0.0949, 0.8962)\n centers[21] = (0.2999, 0.8903)\n centers[22] = (0.5123, 0.8978)\n centers[23] = (0.7005, 0.9245)\n centers[24] = (0.8949, 0.8951)\n centers[25] = (0.0409, 0.0409)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.917558668809064
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/results/packing_viz.png",
+ "execution_time_mean": 88.71411704784259,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..40f0eecdf5fb10b0a487e52c7cd008785282513d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_73/rewrite.txt
@@ -0,0 +1,270 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm uses a multi-start strategy with an "Adaptive Composite Initialization"
+combined with a fully vectorized, aggressive-then-conservative force-directed simulation
+with advanced annealing schedules.
+"""
+
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+
+ # Iterative update for proportional scaling, proven robust
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Centers are practically identical
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or (updated and np.max(np.abs(radii - old_radii)) < convergence_threshold):
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+def initialize_centers_strategy(n, strategy_type, perturbation_scale):
+ """
+ Generates initial center positions based on a chosen strategy.
+ """
+ centers = np.zeros((n, 2))
+
+ if strategy_type == 'random_scatter':
+ # All circles are randomly placed, then undergo a light pre-dispersion FDS
+ centers = np.random.rand(n, 2)
+
+ # Apply a very short, aggressive repulsion to spread them out
+ temp_lr = 0.015 # Higher temp LR
+ temp_growth = 1.05 # Stronger temp growth
+ temp_wall_strength = 0.6 # Stronger temp walls
+ temp_iterations = 75 # More iterations for better initial spread
+
+ for _ in range(temp_iterations):
+ temp_radii = compute_max_radii(centers, max_iter=75, convergence_threshold=1e-4) # Fast radii for temp sim
+ inflated_temp_radii = temp_radii * temp_growth
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent div by zero for identical centers
+
+ sum_inflated_radii = inflated_temp_radii[:, np.newaxis] + inflated_temp_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / dists[:, :, np.newaxis]
+ unit_vectors[~np.isfinite(unit_vectors)] = 0 # Forces to zero for identical centers
+
+ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
+
+ # Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers)
+ overlap_left = inflated_temp_radii - centers[:, 0]
+ overlap_right = (centers[:, 0] + inflated_temp_radii) - 1.0
+ overlap_bottom = inflated_temp_radii - centers[:, 1]
+ overlap_top = (centers[:, 1] + inflated_temp_radii) - 1.0
+
+ wall_forces[:, 0] += temp_wall_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= temp_wall_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += temp_wall_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= temp_wall_strength * np.maximum(0, overlap_top)
+
+ centers += (circle_forces + wall_forces) * temp_lr
+ else: # 'central_void' or 'corner_void' strategies
+ # Calculate number of cells for a grid (sqrt(n-1))
+ # For n=26, n-1=25, so sqrt(25)=5
+ num_grid_circles = n - 1 # Most circles will be placed on a grid
+ num_cells_side = int(np.ceil(np.sqrt(num_grid_circles)))
+ spacing = 1.0 / num_cells_side
+
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < num_grid_circles:
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Perturb the base circles
+ centers[:num_grid_circles, :] += np.random.normal(0, spacing * perturbation_scale, size=(num_grid_circles, 2))
+
+ # Place the last circle based on strategy
+ if strategy_type == 'central_void':
+ centers[n-1] = [0.5, 0.5] + np.random.normal(0, spacing * perturbation_scale, size=2)
+ elif strategy_type == 'corner_void':
+ corners = [[0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95]]
+ chosen_corner = corners[np.random.randint(len(corners))]
+ centers[n-1] = chosen_corner + np.random.normal(0, spacing * perturbation_scale * 0.5, size=2)
+
+ return np.clip(centers, 0.0, 1.0)
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers. Uses Adaptive Composite Initialization
+ and a two-phase, fully vectorized force-directed simulation.
+ """
+ n = 26
+
+ # --- Simulation Hyperparameters ---
+ # Main Phase: Aggressive Exploration
+ main_iterations = 2500
+ main_learning_rate_start = 0.025
+ main_learning_rate_end = 0.001
+ initial_growth_pressure = 1.04
+ final_growth_pressure = 1.0001
+ wall_repulsion_strength_main = 0.4
+
+ # Fine-tuning Phase: Conservative Exploitation
+ fine_tune_iterations = 700
+ fine_tune_learning_rate_start = 0.001
+ fine_tune_learning_rate_end = 0.0001
+ finetune_growth_pressure_start = 1.0005
+ finetune_growth_pressure_end = 1.000001
+ wall_repulsion_strength_fine_tune = 0.3
+
+ # Multi-start parameters
+ num_trials = 12 # Increased trials for better global search
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Define initialization strategies
+ init_strategies = ['central_void', 'corner_void', 'random_scatter']
+
+ for trial in range(num_trials):
+ # Adaptive Composite Initialization: Randomly choose a strategy
+ chosen_strategy = np.random.choice(init_strategies)
+ current_centers = initialize_centers_strategy(n, chosen_strategy, perturbation_scale=0.012) # Slightly increased perturbation
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Physical Simulation Loop (Vectorized) ---
+ for sim_iter in range(main_iterations):
+ # Anneal learning rate (cubic decay for aggressive start, faster reduction)
+ current_lr_main = main_learning_rate_end + \
+ (main_learning_rate_start - main_learning_rate_end) * \
+ (1.0 - (sim_iter / main_iterations))**3
+
+ # Anneal growth pressure (cubic decay for aggressive start, faster reduction)
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**3
+
+ radii_for_sim = compute_max_radii(centers_for_sim, max_iter=150, convergence_threshold=1e-4) # Faster radii calc for simulation
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # --- Vectorized Circle-to-circle repulsion forces ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent div by zero for identical centers
+
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / dists[:, :, np.newaxis]
+ unit_vectors[~np.isfinite(unit_vectors)] = 0 # Forces to zero for identical centers
+
+ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
+
+ # --- Vectorized Wall repulsion forces ---
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength_main * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength_main * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength_main * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength_main * np.maximum(0, overlap_top)
+
+ # Update center positions
+ centers_for_sim += (circle_forces + wall_forces) * current_lr_main
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ # Anneal learning rate (linear decay for stability)
+ current_lr_fine_tune = fine_tune_learning_rate_end + \
+ (fine_tune_learning_rate_start - fine_tune_learning_rate_end) * \
+ (1.0 - (fine_tune_iter / fine_tune_iterations))
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_end + \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (1.0 - (fine_tune_iter / fine_tune_iterations))
+
+ radii_for_sim = compute_max_radii(centers_for_sim, max_iter=150, convergence_threshold=1e-4) # Faster radii calc
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ # --- Vectorized Circle-to-circle repulsion forces ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent div by zero
+
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / dists[:, :, np.newaxis]
+ unit_vectors[~np.isfinite(unit_vectors)] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[:, :, np.newaxis], axis=1)
+
+ # --- Vectorized Wall repulsion forces ---
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength_fine_tune * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength_fine_tune * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength_fine_tune * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength_fine_tune * np.maximum(0, overlap_top)
+
+ # Update center positions
+ centers_for_sim += (circle_forces + wall_forces) * current_lr_fine_tune
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim, max_iter=1000) # High precision final radii
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # Final check: if no suitable solution found, default to a basic one.
+ if best_final_centers is None:
+ best_final_centers = initialize_centers_strategy(n, 'central_void', perturbation_scale=0.01) # A fallback
+
+ final_radii = compute_max_radii(best_final_centers, max_iter=1500) # Very high precision for final output
+
+ return best_final_centers, final_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..26d4e13c38edf87d826cb7c1d1f1b1825e5cb545
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..2b617623331ddfae7c607a43d79a1484ab5c7d01
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/edit.diff
@@ -0,0 +1,279 @@
+--- a/original.py
++++ b/original.py
+@@ -1,251 +1,260 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(iterations):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0]) # Left wall
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0) # Right wall
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1]) # Bottom wall
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self, initial_centers):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self.centers = initial_centers.copy()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers with high precision.
+ self.radii = self._compute_radii_for_centers(self.centers, self.config['final_radius_iter'])
+ return self.centers, self.radii
+
+ def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation uses a multi-start
+ strategy with a ConfigurableHybridPacker to explore various initial configurations
+ and select the best one.
+ """
+ n = 26
+ # Hyperparameters for the force-directed simulation
+ packer_config = {
+ 'sim_iter': 3000, # Increased iterations for thorough refinement.
+ 'radius_sim_iter': 200, # Iterations for radius calculation during simulation (speed).
+ 'final_radius_iter': 750, # Iterations for final radius calculation (precision).
+ 'learning_rate': 0.015, # Slightly adjusted learning rate.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+- 'initial_growth_pressure': 1.03, # Start with stronger pressure to 'unfreeze' the grid.
++ 'initial_growth_pressure': 1.04, # Increased pressure to better 'unfreeze' the grid.
+ 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
+ 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
++ 'num_perturbation_trials': 4, # Number of random perturbations per candidate start
++ 'perturbation_scale': 0.005, # Scale of initial random perturbation
+ }
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points, corner-offset points, and central locations.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2) - primary interstitial
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.5, 0.5], # Center of the square
+ [spacing, 0.5], # Mid-edge interstitial
+ [0.5, spacing],
+ [1 - spacing, 0.5],
+ [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], # Near corners
+ [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Slightly more away from exact corner
+ [0.2, 0.4], [0.4, 0.2] # Additional interstitial points
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+- # Iterate through candidate positions for the 26th circle and run the full packing
++ # Iterate through candidate positions for the 26th circle, and for each,
++ # run multiple trials with random perturbations to break initial grid symmetry.
+ for extra_pos in candidate_extra_positions:
+- current_initial_centers = np.zeros((n, 2))
+- current_initial_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+- current_initial_centers[n-1] = np.array(extra_pos)
+-
+- # Create a new packer instance for each initial configuration
+- packer = ConfigurableHybridPacker(n=n, config=packer_config)
+-
+- centers_candidate, radii_candidate = packer.pack(current_initial_centers)
+- current_sum_radii = np.sum(radii_candidate)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers = centers_candidate.copy()
+- best_radii = radii_candidate.copy()
++ for _ in range(packer_config['num_perturbation_trials']):
++ current_initial_centers = np.zeros((n, 2))
++
++ # Add random perturbation to the base 5x5 grid to break symmetry
++ perturbation = np.random.normal(0, packer_config['perturbation_scale'], size=base_centers.shape)
++ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
++
++ current_initial_centers[:n-1] = perturbed_base_centers
++ current_initial_centers[n-1] = np.array(extra_pos)
++
++ # Create a new packer instance for each stochastic initial configuration
++ packer = ConfigurableHybridPacker(n=n, config=packer_config)
++
++ centers_candidate, radii_candidate = packer.pack(current_initial_centers)
++ current_sum_radii = np.sum(radii_candidate)
++
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_centers = centers_candidate.copy()
++ best_radii = radii_candidate.copy()
+
+ # Fallback if no valid configuration was found (should not happen with comprehensive candidates)
+ if best_centers is None:
+ # Default to a simple initial setup and run if all candidate positions fail unexpectedly
+ default_initial_centers = np.zeros((n, 2))
+ default_initial_centers[:n-1] = base_centers
+ default_initial_centers[n-1] = [0.5, 0.5] # Central placement for 26th circle
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+ best_centers, best_radii = packer.pack(default_initial_centers)
+
+ return best_centers, best_radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..d327126c9be6753c5a3ccc47292963a95019cfe8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/main.py
@@ -0,0 +1,260 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(iterations):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0]) # Left wall
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0) # Right wall
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1]) # Bottom wall
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self, initial_centers):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self.centers = initial_centers.copy()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers with high precision.
+ self.radii = self._compute_radii_for_centers(self.centers, self.config['final_radius_iter'])
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation uses a multi-start
+ strategy with a ConfigurableHybridPacker to explore various initial configurations
+ and select the best one.
+ """
+ n = 26
+ # Hyperparameters for the force-directed simulation
+ packer_config = {
+ 'sim_iter': 3000, # Increased iterations for thorough refinement.
+ 'radius_sim_iter': 200, # Iterations for radius calculation during simulation (speed).
+ 'final_radius_iter': 750, # Iterations for final radius calculation (precision).
+ 'learning_rate': 0.015, # Slightly adjusted learning rate.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.04, # Increased pressure to better 'unfreeze' the grid.
+ 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
+ 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ 'num_perturbation_trials': 4, # Number of random perturbations per candidate start
+ 'perturbation_scale': 0.005, # Scale of initial random perturbation
+ }
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points, corner-offset points, and central locations.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2) - primary interstitial
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.5, 0.5], # Center of the square
+ [spacing, 0.5], # Mid-edge interstitial
+ [0.5, spacing],
+ [1 - spacing, 0.5],
+ [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], # Near corners
+ [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Slightly more away from exact corner
+ [0.2, 0.4], [0.4, 0.2] # Additional interstitial points
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle, and for each,
+ # run multiple trials with random perturbations to break initial grid symmetry.
+ for extra_pos in candidate_extra_positions:
+ for _ in range(packer_config['num_perturbation_trials']):
+ current_initial_centers = np.zeros((n, 2))
+
+ # Add random perturbation to the base 5x5 grid to break symmetry
+ perturbation = np.random.normal(0, packer_config['perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+
+ current_initial_centers[:n-1] = perturbed_base_centers
+ current_initial_centers[n-1] = np.array(extra_pos)
+
+ # Create a new packer instance for each stochastic initial configuration
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+
+ centers_candidate, radii_candidate = packer.pack(current_initial_centers)
+ current_sum_radii = np.sum(radii_candidate)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_candidate.copy()
+ best_radii = radii_candidate.copy()
+
+ # Fallback if no valid configuration was found (should not happen with comprehensive candidates)
+ if best_centers is None:
+ # Default to a simple initial setup and run if all candidate positions fail unexpectedly
+ default_initial_centers = np.zeros((n, 2))
+ default_initial_centers[:n-1] = base_centers
+ default_initial_centers[n-1] = [0.5, 0.5] # Central placement for 26th circle
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+ best_centers, best_radii = packer.pack(default_initial_centers)
+
+ return best_centers, best_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..18d9ce514e2e575709de3d29aac09ef53019707d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/original.py
@@ -0,0 +1,251 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(iterations):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0]) # Left wall
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0) # Right wall
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1]) # Bottom wall
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self, initial_centers):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self.centers = initial_centers.copy()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers with high precision.
+ self.radii = self._compute_radii_for_centers(self.centers, self.config['final_radius_iter'])
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation uses a multi-start
+ strategy with a ConfigurableHybridPacker to explore various initial configurations
+ and select the best one.
+ """
+ n = 26
+ # Hyperparameters for the force-directed simulation
+ packer_config = {
+ 'sim_iter': 3000, # Increased iterations for thorough refinement.
+ 'radius_sim_iter': 200, # Iterations for radius calculation during simulation (speed).
+ 'final_radius_iter': 750, # Iterations for final radius calculation (precision).
+ 'learning_rate': 0.015, # Slightly adjusted learning rate.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.03, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
+ 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points, corner-offset points, and central locations.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2) - primary interstitial
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.5, 0.5], # Center of the square
+ [spacing, 0.5], # Mid-edge interstitial
+ [0.5, spacing],
+ [1 - spacing, 0.5],
+ [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], # Near corners
+ [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Slightly more away from exact corner
+ [0.2, 0.4], [0.4, 0.2] # Additional interstitial points
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle and run the full packing
+ for extra_pos in candidate_extra_positions:
+ current_initial_centers = np.zeros((n, 2))
+ current_initial_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+ current_initial_centers[n-1] = np.array(extra_pos)
+
+ # Create a new packer instance for each initial configuration
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+
+ centers_candidate, radii_candidate = packer.pack(current_initial_centers)
+ current_sum_radii = np.sum(radii_candidate)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_candidate.copy()
+ best_radii = radii_candidate.copy()
+
+ # Fallback if no valid configuration was found (should not happen with comprehensive candidates)
+ if best_centers is None:
+ # Default to a simple initial setup and run if all candidate positions fail unexpectedly
+ default_initial_centers = np.zeros((n, 2))
+ default_initial_centers[:n-1] = base_centers
+ default_initial_centers[n-1] = [0.5, 0.5] # Central placement for 26th circle
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+ best_centers, best_radii = packer.pack(default_initial_centers)
+
+ return best_centers, best_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..eb820925bac441b39b0d1023e8c46ebd0ffd6e6f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results
+Run 1/1 completed in 714.23 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.01237695492434
+ public: {'centers_str': ' centers[0] = (0.0973, 0.0938)\n centers[1] = (0.2652, 0.0703)\n centers[2] = (0.5291, 0.0720)\n centers[3] = (0.7035, 0.0966)\n centers[4] = (0.8986, 0.0954)\n centers[5] = (0.0996, 0.2926)\n centers[6] = (0.2790, 0.3303)\n centers[7] = (0.5221, 0.3226)\n centers[8] = (0.6950, 0.3064)\n centers[9] = (0.8906, 0.2999)\n centers[10] = (0.1056, 0.4920)\n centers[11] = (0.3020, 0.5123)\n centers[12] = (0.4993, 0.5032)\n centers[13] = (0.6895, 0.4710)\n centers[14] = (0.8999, 0.5092)\n centers[15] = (0.1008, 0.6963)\n centers[16] = (0.2638, 0.6792)\n centers[17] = (0.4516, 0.6776)\n centers[18] = (0.7182, 0.6936)\n centers[19] = (0.9382, 0.7101)\n centers[20] = (0.0849, 0.9038)\n centers[21] = (0.2901, 0.8785)\n centers[22] = (0.5245, 0.8874)\n centers[23] = (0.7074, 0.9261)\n centers[24] = (0.8932, 0.8933)\n centers[25] = (0.3981, 0.1982)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.01237695492434}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/packing_viz.png
+ execution_time_mean: 714.2297050110064
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..0580a811cfe13a110d49bda8fd796bb91cbe6945
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.01237695492434,
+ "public": {
+ "centers_str": " centers[0] = (0.0973, 0.0938)\n centers[1] = (0.2652, 0.0703)\n centers[2] = (0.5291, 0.0720)\n centers[3] = (0.7035, 0.0966)\n centers[4] = (0.8986, 0.0954)\n centers[5] = (0.0996, 0.2926)\n centers[6] = (0.2790, 0.3303)\n centers[7] = (0.5221, 0.3226)\n centers[8] = (0.6950, 0.3064)\n centers[9] = (0.8906, 0.2999)\n centers[10] = (0.1056, 0.4920)\n centers[11] = (0.3020, 0.5123)\n centers[12] = (0.4993, 0.5032)\n centers[13] = (0.6895, 0.4710)\n centers[14] = (0.8999, 0.5092)\n centers[15] = (0.1008, 0.6963)\n centers[16] = (0.2638, 0.6792)\n centers[17] = (0.4516, 0.6776)\n centers[18] = (0.7182, 0.6936)\n centers[19] = (0.9382, 0.7101)\n centers[20] = (0.0849, 0.9038)\n centers[21] = (0.2901, 0.8785)\n centers[22] = (0.5245, 0.8874)\n centers[23] = (0.7074, 0.9261)\n centers[24] = (0.8932, 0.8933)\n centers[25] = (0.3981, 0.1982)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.01237695492434
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/results/packing_viz.png",
+ "execution_time_mean": 714.2297050110064,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..924c1ac5e119a208a1af75cbcb743c98d7407aa1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_74/search_replace.txt
@@ -0,0 +1,81 @@
+
+stochastic_multistart_perturbation
+
+
+
+The current algorithm, despite its multi-start strategy, is too deterministic and gets stuck in a local minimum corresponding to the initial 5x5 grid configuration. This is evident from the low score and the grid-like visual result.
+
+To overcome this, I am introducing two key changes inspired by successful prior evolutionary algorithms and physical simulation principles:
+
+1. **Stochastic Initial Perturbation**: For each candidate starting position of the 26th circle, the algorithm will now run multiple trials. In each trial, a small random perturbation is added to the initial positions of the 25 circles on the grid. This breaks the perfect symmetry of the starting configuration, which is crucial for allowing the force-directed simulation to explore more diverse and potentially denser, non-grid-like arrangements.
+
+2. **Increased Initial Repulsion**: I am increasing the `initial_growth_pressure` from `1.03` to `1.04`. This makes the initial "explosion" phase more aggressive, providing a stronger force to push circles out of their initial suboptimal grid positions and "melt" the configuration, allowing it to resettle into a more efficient packing.
+
+Together, these changes transform the deterministic multi-start into a stochastic one, significantly enhancing the exploration capabilities of the algorithm and increasing its chances of discovering a much better packing.
+
+
+
+<<<<<<< SEARCH
+ 'learning_rate': 0.015, # Slightly adjusted learning rate.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.03, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
+ 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+=======
+ 'learning_rate': 0.015, # Slightly adjusted learning rate.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.04, # Increased pressure to better 'unfreeze' the grid.
+ 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
+ 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ 'num_perturbation_trials': 4, # Number of random perturbations per candidate start
+ 'perturbation_scale': 0.005, # Scale of initial random perturbation
+ }
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ # Iterate through candidate positions for the 26th circle and run the full packing
+ for extra_pos in candidate_extra_positions:
+ current_initial_centers = np.zeros((n, 2))
+ current_initial_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+ current_initial_centers[n-1] = np.array(extra_pos)
+
+ # Create a new packer instance for each initial configuration
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+
+ centers_candidate, radii_candidate = packer.pack(current_initial_centers)
+ current_sum_radii = np.sum(radii_candidate)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_candidate.copy()
+ best_radii = radii_candidate.copy()
+=======
+ # Iterate through candidate positions for the 26th circle, and for each,
+ # run multiple trials with random perturbations to break initial grid symmetry.
+ for extra_pos in candidate_extra_positions:
+ for _ in range(packer_config['num_perturbation_trials']):
+ current_initial_centers = np.zeros((n, 2))
+
+ # Add random perturbation to the base 5x5 grid to break symmetry
+ perturbation = np.random.normal(0, packer_config['perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+
+ current_initial_centers[:n-1] = perturbed_base_centers
+ current_initial_centers[n-1] = np.array(extra_pos)
+
+ # Create a new packer instance for each stochastic initial configuration
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+
+ centers_candidate, radii_candidate = packer.pack(current_initial_centers)
+ current_sum_radii = np.sum(radii_candidate)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_candidate.copy()
+ best_radii = radii_candidate.copy()
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5ebcbf1050fbfbeb3c40359d4e33c6eea41a04ba
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..eb6f7547f099f293522afad5a3bbd5627a4795e2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/edit.diff
@@ -0,0 +1,326 @@
+--- a/original.py
++++ b/original.py
+@@ -1,234 +1,283 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ class LocalSearchRefiner:
+- """A class to perform fast, local refinement on a set of centers."""
+- def __init__(self, iterations, learning_rate, wall_strength):
+- self.iterations = iterations
+- self.lr = learning_rate
+- self.wall_strength = wall_strength
++ """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
++ def __init__(self, config):
++ self.config = config
+
+ def refine(self, centers):
+- """Applies a few steps of force-directed refinement to polish a solution."""
++ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+- for _ in range(self.iterations):
+- # Use faster radius calculation for local search
+- radii = compute_max_radii(refined_centers, max_iter=100)
+-
+- # --- Vectorized Force Calculation (no growth pressure) ---
+- # Circle-to-circle forces
++
++ # --- Stage 1: Aggressive settling with subtle growth pressure ---
++ for i_agg_iter in range(self.config['ls_aggressive_iter']):
++ progress = i_agg_iter / self.config['ls_aggressive_iter']
++ current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
++ (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
++ (1.0 - progress)**1.5 # Cubic decay for growth pressure
++
++ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
++ pressured_radii = radii * current_growth_pressure
++
++ # Vectorized Force Calculation
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
++
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ force_mags = overlaps
++ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ wall_forces = np.zeros_like(refined_centers)
++ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
++ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++
++ current_lr_agg = self.config['ls_aggressive_lr_end'] + \
++ (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
++ (1.0 - progress)**2.0 # Quadratic decay for LR
++
++ refined_centers += forces * current_lr_agg
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ # --- Stage 2: Fine-tuning without growth pressure ---
++ for _ in range(self.config['ls_fine_tune_iter']):
++ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
++
++ # Vectorized Force Calculation (no growth pressure)
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+- # Wall forces
+ wall_forces = np.zeros_like(refined_centers)
+- wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+- wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
++ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
++ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+- refined_centers += forces * self.lr
++ refined_centers += forces * self.config['ls_fine_tune_lr']
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+ class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+- self.local_search_refiner = LocalSearchRefiner(
+- iterations=config['ls_iter'],
+- learning_rate=config['ls_lr'],
+- wall_strength=config['ls_wall_strength']
+- )
++ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+- """Performs uniform crossover, taking whole circles from either parent."""
+- child = p1.copy()
+- mask = np.random.rand(self.n) < 0.5
+- child[mask] = p2[mask]
+- return child
+-
+- def _mutate(self, individual, strength):
++ """
++ Performs Blend Crossover (BLX-alpha) on continuous variables.
++ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
++ """
++ # Using a small random range around 0.5 for alpha for averaging blend
++ alpha = np.random.uniform(0.4, 0.6)
++ child = alpha * p1 + (1 - alpha) * p2
++ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
++
++ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+- if np.random.rand() < self.config['mutation_rate']:
++ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+- (1.0 - (gen / self.config['generations']))**2.0
++ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
++
++ current_mutation_rate = self.config['mutation_rate_end'] + \
++ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
++ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+- child = self._mutate(child, mut_strength)
++ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+- 'population_size': 60,
+- 'generations': 300,
++ 'population_size': 80, # Increased population size for more diversity
++ 'generations': 400, # Increased generations for more thorough evolution
+ 'elite_count': 4,
+- 'tournament_size': 5,
+- 'mutation_rate': 0.3,
++ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
++ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
++ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+- 'ls_iter': 30, # Iterations for the local search refinement
+- 'ls_lr': 0.003, # Learning rate for the local search
+- 'ls_wall_strength': 0.2,
++
++ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
++ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
++ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
++ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
++ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
++ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
++ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
++
++ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
++ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
++ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
++ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..e53e50b4f5ba23507c3bd0b593eab9b77ba8edad
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/main.py
@@ -0,0 +1,283 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+
+ # --- Stage 1: Aggressive settling with subtle growth pressure ---
+ for i_agg_iter in range(self.config['ls_aggressive_iter']):
+ progress = i_agg_iter / self.config['ls_aggressive_iter']
+ current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+ (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+ (1.0 - progress)**1.5 # Cubic decay for growth pressure
+
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+ pressured_radii = radii * current_growth_pressure
+
+ # Vectorized Force Calculation
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+ (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+ (1.0 - progress)**2.0 # Quadratic decay for LR
+
+ refined_centers += forces * current_lr_agg
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning without growth pressure ---
+ for _ in range(self.config['ls_fine_tune_iter']):
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+
+ # Vectorized Force Calculation (no growth pressure)
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.config['ls_fine_tune_lr']
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+
+ current_mutation_rate = self.config['mutation_rate_end'] + \
+ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 400, # Increased generations for more thorough evolution
+ 'elite_count': 4,
+ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+
+ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+
+ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd842822cfbe7b9346930cde45e0df1e9fdce374
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/original.py
@@ -0,0 +1,234 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers."""
+ def __init__(self, iterations, learning_rate, wall_strength):
+ self.iterations = iterations
+ self.lr = learning_rate
+ self.wall_strength = wall_strength
+
+ def refine(self, centers):
+ """Applies a few steps of force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ for _ in range(self.iterations):
+ # Use faster radius calculation for local search
+ radii = compute_max_radii(refined_centers, max_iter=100)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # Circle-to-circle forces
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(
+ iterations=config['ls_iter'],
+ learning_rate=config['ls_lr'],
+ wall_strength=config['ls_wall_strength']
+ )
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 60,
+ 'generations': 300,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.3,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+ 'ls_iter': 30, # Iterations for the local search refinement
+ 'ls_lr': 0.003, # Learning rate for the local search
+ 'ls_wall_strength': 0.2,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..738e9f1142350b808567f5ae6357db82d69664b8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results
+Run 1/1 completed in 828.59 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.2657684539056238
+ public: {'centers_str': ' centers[0] = (0.0903, 0.0907)\n centers[1] = (0.3550, 0.1200)\n centers[2] = (0.5455, 0.0756)\n centers[3] = (0.7025, 0.0811)\n centers[4] = (0.8906, 0.1136)\n centers[5] = (0.0987, 0.3443)\n centers[6] = (0.6381, 0.6638)\n centers[7] = (0.4667, 0.2539)\n centers[8] = (0.6980, 0.2866)\n centers[9] = (0.9070, 0.3174)\n centers[10] = (0.0781, 0.5202)\n centers[11] = (0.2565, 0.4294)\n centers[12] = (0.4893, 0.4970)\n centers[13] = (0.7044, 0.5227)\n centers[14] = (0.9048, 0.5066)\n centers[15] = (0.0805, 0.6771)\n centers[16] = (0.2895, 0.6839)\n centers[17] = (0.5285, 0.7066)\n centers[18] = (0.7136, 0.7099)\n centers[19] = (0.8985, 0.7038)\n centers[20] = (0.1200, 0.8786)\n centers[21] = (0.3222, 0.9148)\n centers[22] = (0.4947, 0.9123)\n centers[23] = (0.6860, 0.8956)\n centers[24] = (0.8888, 0.9026)\n centers[25] = (0.2171, 0.2149)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.2657684539056238}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/packing_viz.png
+ execution_time_mean: 828.5881741889752
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..7dba12c8f0d9135eb9aebc36bef81f41ded4887b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.2657684539056238,
+ "public": {
+ "centers_str": " centers[0] = (0.0903, 0.0907)\n centers[1] = (0.3550, 0.1200)\n centers[2] = (0.5455, 0.0756)\n centers[3] = (0.7025, 0.0811)\n centers[4] = (0.8906, 0.1136)\n centers[5] = (0.0987, 0.3443)\n centers[6] = (0.6381, 0.6638)\n centers[7] = (0.4667, 0.2539)\n centers[8] = (0.6980, 0.2866)\n centers[9] = (0.9070, 0.3174)\n centers[10] = (0.0781, 0.5202)\n centers[11] = (0.2565, 0.4294)\n centers[12] = (0.4893, 0.4970)\n centers[13] = (0.7044, 0.5227)\n centers[14] = (0.9048, 0.5066)\n centers[15] = (0.0805, 0.6771)\n centers[16] = (0.2895, 0.6839)\n centers[17] = (0.5285, 0.7066)\n centers[18] = (0.7136, 0.7099)\n centers[19] = (0.8985, 0.7038)\n centers[20] = (0.1200, 0.8786)\n centers[21] = (0.3222, 0.9148)\n centers[22] = (0.4947, 0.9123)\n centers[23] = (0.6860, 0.8956)\n centers[24] = (0.8888, 0.9026)\n centers[25] = (0.2171, 0.2149)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.2657684539056238
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/results/packing_viz.png",
+ "execution_time_mean": 828.5881741889752,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..17cc4285070e73666a128d4860a2e47602fd4950
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_75/search_replace.txt
@@ -0,0 +1,317 @@
+
+two_stage_local_search
+
+
+
+The `LocalSearchRefiner` is modified to implement a two-stage force-directed simulation, as inspired by Recommendation #4.
+The first stage ("aggressive") uses a higher learning rate and a subtle growth pressure, both annealed, for a short number of iterations to quickly resolve major overlaps and push circles apart.
+The second stage ("fine-tuning") uses a very low, fixed learning rate and no growth pressure for more iterations to meticulously settle the circles and remove any remaining minor overlaps, ensuring precise placement.
+New hyperparameters for both stages are added to the main configuration dictionary in `construct_packing`. This approach makes the local search more robust and effective at improving candidate solutions.
+
+
+
+<<<<<<< SEARCH
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers."""
+ def __init__(self, iterations, learning_rate, wall_strength):
+ self.iterations = iterations
+ self.lr = learning_rate
+ self.wall_strength = wall_strength
+
+ def refine(self, centers):
+ """Applies a few steps of force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ for _ in range(self.iterations):
+ # Use faster radius calculation for local search
+ radii = compute_max_radii(refined_centers, max_iter=100)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ # Circle-to-circle forces
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+=======
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+
+ # --- Stage 1: Aggressive settling with subtle growth pressure ---
+ for i_agg_iter in range(self.config['ls_aggressive_iter']):
+ progress = i_agg_iter / self.config['ls_aggressive_iter']
+ current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+ (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+ (1.0 - progress)**1.5 # Cubic decay for growth pressure
+
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+ pressured_radii = radii * current_growth_pressure
+
+ # Vectorized Force Calculation
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+ (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+ (1.0 - progress)**2.0 # Quadratic decay for LR
+
+ refined_centers += forces * current_lr_agg
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning without growth pressure ---
+ for _ in range(self.config['ls_fine_tune_iter']):
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+
+ # Vectorized Force Calculation (no growth pressure)
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.config['ls_fine_tune_lr']
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+>>>>>>> REPLACE
+
+
+
+update_memetic_algo_init
+
+
+
+Modify the `MemeticAlgorithm`'s `__init__` method to pass the entire `config` dictionary to the `LocalSearchRefiner`. This makes the `LocalSearchRefiner`'s initialization more flexible and allows it to access all necessary hyperparameters for its new two-stage refinement process.
+
+
+
+<<<<<<< SEARCH
+ self.local_search_refiner = LocalSearchRefiner(
+ iterations=config['ls_iter'],
+ learning_rate=config['ls_lr'],
+ wall_strength=config['ls_wall_strength']
+ )
+=======
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+>>>>>>> REPLACE
+
+
+
+replace_uniform_with_blend_crossover
+
+
+
+Replaces the existing uniform crossover with Blend Crossover (BLX-alpha) as per Recommendation #2. BLX-alpha is more suitable for continuous optimization problems like circle packing, as it generates offspring by blending the parent coordinates rather than simply swapping entire circles. This allows for finer-grained exploration of the solution space between good parent solutions. A fixed `alpha` of 0.5 is used for now, representing a simple averaging, but this could be made adaptive later.
+
+
+
+<<<<<<< SEARCH
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+=======
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+>>>>>>> REPLACE
+
+
+
+update_mutate_signature_and_usage
+
+
+
+Modifies the `_mutate` method to accept `mutation_rate_per_circle` as a parameter, reflecting the new adaptive annealing schedule. The internal usage within `_mutate` is updated to use this passed rate, ensuring consistency with the annealing logic.
+
+
+
+<<<<<<< SEARCH
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+=======
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+>>>>>>> REPLACE
+
+
+
+update_construct_packing_config
+
+
+
+Updates the `config` dictionary in `construct_packing` to adjust core Genetic Algorithm hyperparameters and introduce new parameters for the two-stage `LocalSearchRefiner`.
+- `generations` increased to 400 and `population_size` to 80 for broader search and convergence.
+- `tournament_size` increased to 7 for stronger selection pressure.
+- `mutation_rate` is now an initial value, with `mutation_rate_end` defining the final annealed rate (Recommendation #5).
+- New `ls_aggressive_iter`, `ls_aggressive_lr_start`, `ls_aggressive_lr_end`, `ls_aggressive_growth_pressure_start`, `ls_aggressive_growth_pressure_end`, `ls_radius_iter_agg` are added for the first stage of local search.
+- New `ls_fine_tune_iter`, `ls_fine_tune_lr`, `ls_radius_iter_ft` are added for the second stage of local search.
+- `ls_wall_strength` is now defined here and accessed by the refiner.
+
+
+
+<<<<<<< SEARCH
+ 'population_size': 60,
+ 'generations': 300,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.3,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+ 'ls_iter': 30, # Iterations for the local search refinement
+ 'ls_lr': 0.003, # Learning rate for the local search
+ 'ls_wall_strength': 0.2,
+=======
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 400, # Increased generations for more thorough evolution
+ 'elite_count': 4,
+ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+
+ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+
+ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
+>>>>>>> REPLACE
+
+
+
+anneal_mutation_rate
+
+
+
+Implements adaptive `mutation_rate` by annealing it over generations, as suggested in Recommendation #5. This allows for higher exploration (more mutation) in early generations and finer tuning (less mutation) in later generations, helping to prevent premature convergence and encourage convergence to high-quality solutions. The `_mutate` function call is updated to pass this annealed mutation rate.
+
+
+
+<<<<<<< SEARCH
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+=======
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+
+ current_mutation_rate = self.config['mutation_rate_end'] + \
+ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..69d7a2fd41957ae9a9f6e6766bb944a6089c0b0b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/edit.diff
@@ -0,0 +1,423 @@
+--- a/original.py
++++ b/original.py
+@@ -1,218 +1,222 @@
+ # EVOLVE-BLOCK-START
+-"""
+-Physical simulation-based circle packing for n=26 circles.
+-This algorithm is a crossover, combining the high-level strategy of the best-performing
+-parent (multi-start, two-phase simulation, annealed growth pressure) with the
+-efficient vectorized force calculations from the inspiration program.
+-"""
+-
+ import numpy as np
+
+-def construct_packing():
++# Utility function to compute radii, remains a core component
++def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-9):
+ """
+- Constructs a packing of 26 circles by simulating physical repulsion forces
+- to find an optimal arrangement of centers.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- n = 26
+-
+- # --- Hyperparameters for the simulation ---
+- # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+- iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
+- learning_rate = 0.015 # Increased learning rate for more significant center movements
+- wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
+- initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
+- final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+-
+- best_sum_radii = -1.0
+- best_final_centers = None
+-
+- # Candidate initial positions for the 26th circle to optimize corner utilization.
+- candidate_extra_circle_initial_positions = [
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+- [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+- ]
+-
+- for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+- # --- Initialization for current trial ---
+- current_centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- current_centers[k, 0] = (i + 0.5) * spacing
+- current_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- # Add small random perturbation to the 5x5 grid (first 25 circles, re-introduced from successful prior)
+- perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
+- current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+-
+- current_centers[25] = initial_pos_26th_circle
+-
+- # Ensure all centers stay within bounds after initial setup and perturbation
+- current_centers = np.clip(current_centers, 0.0, 1.0)
+-
+- centers_for_sim = current_centers.copy()
+-
+- # --- Main Simulation Loop (Vectorized) ---
+- for sim_iter in range(iterations):
+- radii = compute_max_radii(centers_for_sim)
+-
+- # Anneal growth pressure quadratically
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * \
+- (1.0 - (sim_iter / iterations))**2
+- inflated_radii = radii * current_growth_pressure
+-
+- # --- Vectorized Force Calculation ---
+- # a. Circle-to-circle repulsion forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_inflated_radii - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- # b. Vectorized Wall repulsion forces
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = inflated_radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+- overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces = circle_forces + wall_forces
+-
+- # --- Update center positions ---
+- current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+- centers_for_sim += forces * current_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # --- Finalization for current trial ---
+- current_final_radii = compute_max_radii(centers_for_sim)
+- current_sum_radii = np.sum(current_final_radii)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_final_centers = centers_for_sim.copy()
+-
+- # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+- fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+- finetune_growth_pressure_start = 1.0005 # Subtle growth pressure
+- finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
+- centers_for_sim = best_final_centers.copy()
+-
+- for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+- radii = compute_max_radii(centers_for_sim)
+-
+- # Anneal subtle growth pressure (linear decay for stability)
+- current_finetune_growth_pressure = finetune_growth_pressure_start - \
+- (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+- (sim_iter_ft / fine_tune_iterations)
+- inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+-
+- # --- Vectorized Force Calculation ---
+- # a. Circle-to-circle forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+-
+- overlaps = np.maximum(0, sum_radii_pairs - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- # b. Wall forces
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+- overlap_bottom = radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces = circle_forces + wall_forces
+-
+- # Anneal learning rate for fine-tuning (linear decay)
+- current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+- centers_for_sim += forces * current_finetune_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- final_centers = centers_for_sim
+- final_radii = compute_max_radii(final_centers)
+-
+- return final_centers, final_radii
+-
+-
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This uses an iterative proportional scaling method.
++ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+- radii = np.ones(n)
+-
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- for _ in range(500):
+- updated_in_pass = False
++ if n == 0:
++ return np.array([])
++ # Initial radii are limited by the walls
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ for _ in range(max_iter):
++ old_radii = radii.copy()
++ updated = False
++ # Iteratively shrink radii that cause overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9:
+- radii[i] = 0.0
+- radii[j] = 0.0
+- updated_in_pass = True
+- continue
+-
++ dist_sq = np.sum((centers[i] - centers[j])**2)
++ dist = np.sqrt(dist_sq)
++
+ if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- if not updated_in_pass:
++ if dist < 1e-9: # Handle co-located centers
++ radii[i], radii[j] = 0.0, 0.0
++ else:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++
++ # Check for convergence
++ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+-
+- return np.maximum(radii, 0.0)
+-
++
++ return np.maximum(radii, 0)
++
++class FitnessEvaluator:
++ """Evaluates a solution vector by running local search and computing radii."""
++ def __init__(self, n, ls_config):
++ self.n = n
++ self.ls_iter = ls_config['iterations']
++ self.ls_lr = ls_config['learning_rate']
++ self.wall_strength = ls_config['wall_strength']
++
++ def refine(self, centers):
++ """Applies a few steps of force-directed refinement to polish a solution."""
++ refined_centers = centers.copy()
++ for _ in range(self.ls_iter):
++ # Use a quick radius calculation for local search
++ radii = compute_max_radii(refined_centers, max_iter=100)
++
++ # --- Vectorized Force Calculation (no growth pressure) ---
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ force_mags = overlaps
++ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ # Wall forces
++ wall_forces = np.zeros_like(refined_centers)
++ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
++ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++
++ refined_centers += forces * self.ls_lr
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ return refined_centers
++
++ def evaluate(self, solution_vector):
++ """The main fitness evaluation function for a solution vector."""
++ centers = np.clip(solution_vector.reshape(self.n, 2), 0.0, 1.0)
++
++ # Memetic step: apply local search
++ refined_centers = self.refine(centers)
++
++ # Calculate radii for the refined centers
++ radii = compute_max_radii(refined_centers, max_iter=250)
++
++ # CMA-ES minimizes, so we return the negative sum of radii
++ return -np.sum(radii)
++
++class CMAESPacker:
++ """Implements the CMA-ES algorithm for circle packing."""
++ def __init__(self, x_start, sigma, population_size=None):
++ self.dim = len(x_start)
++ # Set population size (lambda)
++ self.popsize = population_size or 4 + int(3 * np.log(self.dim))
++ # Set number of parents (mu)
++ self.mu = self.popsize // 2
++
++ # Initialize weights for recombination
++ weights = np.log(self.mu + 0.5) - np.log(np.arange(1, self.mu + 1))
++ self.weights = weights / np.sum(weights)
++ self.mueff = np.sum(self.weights)**2 / np.sum(self.weights**2)
++
++ # Initialize strategy parameters for adaptation
++ self.mean = x_start.copy()
++ self.sigma = sigma
++ self.pc = np.zeros(self.dim)
++ self.ps = np.zeros(self.dim)
++ self.C = np.eye(self.dim)
++ self.B = np.eye(self.dim)
++ self.D = np.ones(self.dim)
++ self.chiN = self.dim**0.5 * (1 - 1 / (4 * self.dim) + 1 / (21 * self.dim**2))
++ self.generations = 0
++
++ # Learning rates
++ self.cc = (4 + self.mueff / self.dim) / (self.dim + 4 + 2 * self.mueff / self.dim)
++ self.cs = (self.mueff + 2) / (self.dim + self.mueff + 5)
++ self.c1 = 2 / ((self.dim + 1.3)**2 + self.mueff)
++ self.cmu = min(1 - self.c1, 2 * (self.mueff - 2 + 1 / self.mueff) / ((self.dim + 2)**2 + self.mueff))
++ self.damps = 1 + 2 * max(0, np.sqrt((self.mueff - 1) / (self.dim + 1)) - 1) + self.cs
++
++ def ask(self):
++ """Generate a new population of candidate solutions."""
++ self.solutions = []
++ for _ in range(self.popsize):
++ z = np.random.randn(self.dim)
++ y = self.B @ (self.D * z)
++ x = self.mean + self.sigma * y
++ self.solutions.append(x)
++ return self.solutions
++
++ def tell(self, fitnesses):
++ """Update the internal state based on the fitness of the last population."""
++ self.generations += 1
++ sorted_indices = np.argsort(fitnesses)
++
++ old_mean = self.mean.copy()
++ best_solutions = np.array(self.solutions)[sorted_indices[:self.mu]]
++ self.mean = self.weights @ best_solutions
++
++ y_w = (self.mean - old_mean) / self.sigma
++
++ # Step-size control
++ self.ps = (1 - self.cs) * self.ps + np.sqrt(self.cs * (2 - self.cs) * self.mueff) * (self.B @ y_w)
++ self.sigma *= np.exp((self.cs / self.damps) * (np.linalg.norm(self.ps) / self.chiN - 1))
++
++ # Covariance matrix adaptation
++ h_sig = int(np.linalg.norm(self.ps) / np.sqrt(1 - (1 - self.cs)**(2 * self.generations)) / self.chiN < 1.4 + 2 / (self.dim + 1))
++ self.pc = (1 - self.cc) * self.pc + h_sig * np.sqrt(self.cc * (2 - self.cc) * self.mueff) * y_w
++
++ artmp = (best_solutions - old_mean) / self.sigma
++ self.C = (1 - self.c1 - self.cmu) * self.C + self.c1 * (np.outer(self.pc, self.pc) + (1 - h_sig) * self.cc * (2 - self.cc) * self.C) + self.cmu * (artmp.T @ np.diag(self.weights) @ artmp)
++
++ # Decompose C into B and D for sampling in the next generation
++ self.D, self.B = np.linalg.eigh(self.C)
++ self.D = np.sqrt(np.maximum(self.D, 1e-12)) # Add small constant to avoid sqrt of negative
++
++def construct_packing():
++ """Constructs a packing of 26 circles using a CMA-ES Memetic Algorithm."""
++ n = 26
++
++ cma_config = {
++ 'generations': 150,
++ 'sigma': 0.1,
++ 'population_size': 20,
++ }
++ ls_config = {
++ 'iterations': 25,
++ 'learning_rate': 0.005,
++ 'wall_strength': 0.2,
++ }
++
++ # Start with a perturbed 5x5 grid + 26th circle in a promising void
++ initial_centers = np.zeros((n, 2))
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for row in range(num_cells_side):
++ for col in range(num_cells_side):
++ initial_centers[k] = [(col + 0.5) * spacing, (row + 0.5) * spacing]
++ k += 1
++ initial_centers[25] = [spacing, spacing]
++ initial_centers += np.random.normal(0, 0.01, size=initial_centers.shape)
++ x0 = np.clip(initial_centers, 0.0, 1.0).flatten()
++
++ packer = CMAESPacker(x_start=x0, sigma=cma_config['sigma'], population_size=cma_config['population_size'])
++ evaluator = FitnessEvaluator(n=n, ls_config=ls_config)
++
++ best_ever_solution = x0
++ best_ever_fitness = evaluator.evaluate(x0)
++
++ for gen in range(cma_config['generations']):
++ solutions = packer.ask()
++ fitnesses = [evaluator.evaluate(s) for s in solutions]
++ packer.tell(fitnesses)
++
++ current_best_idx = np.argmin(fitnesses)
++ if fitnesses[current_best_idx] < best_ever_fitness:
++ best_ever_fitness = fitnesses[current_best_idx]
++ best_ever_solution = solutions[current_best_idx]
++
++ final_centers = np.clip(best_ever_solution.reshape(n, 2), 0.0, 1.0)
++ final_radii = compute_max_radii(final_centers, max_iter=1500)
++
++ return final_centers, final_radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc879aff3361dea0ed2b78dc18255282d0fde058
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/main.py
@@ -0,0 +1,222 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# Utility function to compute radii, remains a core component
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-9):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+ # Initial radii are limited by the walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ # Iteratively shrink radii that cause overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+class FitnessEvaluator:
+ """Evaluates a solution vector by running local search and computing radii."""
+ def __init__(self, n, ls_config):
+ self.n = n
+ self.ls_iter = ls_config['iterations']
+ self.ls_lr = ls_config['learning_rate']
+ self.wall_strength = ls_config['wall_strength']
+
+ def refine(self, centers):
+ """Applies a few steps of force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ for _ in range(self.ls_iter):
+ # Use a quick radius calculation for local search
+ radii = compute_max_radii(refined_centers, max_iter=100)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.ls_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+ def evaluate(self, solution_vector):
+ """The main fitness evaluation function for a solution vector."""
+ centers = np.clip(solution_vector.reshape(self.n, 2), 0.0, 1.0)
+
+ # Memetic step: apply local search
+ refined_centers = self.refine(centers)
+
+ # Calculate radii for the refined centers
+ radii = compute_max_radii(refined_centers, max_iter=250)
+
+ # CMA-ES minimizes, so we return the negative sum of radii
+ return -np.sum(radii)
+
+class CMAESPacker:
+ """Implements the CMA-ES algorithm for circle packing."""
+ def __init__(self, x_start, sigma, population_size=None):
+ self.dim = len(x_start)
+ # Set population size (lambda)
+ self.popsize = population_size or 4 + int(3 * np.log(self.dim))
+ # Set number of parents (mu)
+ self.mu = self.popsize // 2
+
+ # Initialize weights for recombination
+ weights = np.log(self.mu + 0.5) - np.log(np.arange(1, self.mu + 1))
+ self.weights = weights / np.sum(weights)
+ self.mueff = np.sum(self.weights)**2 / np.sum(self.weights**2)
+
+ # Initialize strategy parameters for adaptation
+ self.mean = x_start.copy()
+ self.sigma = sigma
+ self.pc = np.zeros(self.dim)
+ self.ps = np.zeros(self.dim)
+ self.C = np.eye(self.dim)
+ self.B = np.eye(self.dim)
+ self.D = np.ones(self.dim)
+ self.chiN = self.dim**0.5 * (1 - 1 / (4 * self.dim) + 1 / (21 * self.dim**2))
+ self.generations = 0
+
+ # Learning rates
+ self.cc = (4 + self.mueff / self.dim) / (self.dim + 4 + 2 * self.mueff / self.dim)
+ self.cs = (self.mueff + 2) / (self.dim + self.mueff + 5)
+ self.c1 = 2 / ((self.dim + 1.3)**2 + self.mueff)
+ self.cmu = min(1 - self.c1, 2 * (self.mueff - 2 + 1 / self.mueff) / ((self.dim + 2)**2 + self.mueff))
+ self.damps = 1 + 2 * max(0, np.sqrt((self.mueff - 1) / (self.dim + 1)) - 1) + self.cs
+
+ def ask(self):
+ """Generate a new population of candidate solutions."""
+ self.solutions = []
+ for _ in range(self.popsize):
+ z = np.random.randn(self.dim)
+ y = self.B @ (self.D * z)
+ x = self.mean + self.sigma * y
+ self.solutions.append(x)
+ return self.solutions
+
+ def tell(self, fitnesses):
+ """Update the internal state based on the fitness of the last population."""
+ self.generations += 1
+ sorted_indices = np.argsort(fitnesses)
+
+ old_mean = self.mean.copy()
+ best_solutions = np.array(self.solutions)[sorted_indices[:self.mu]]
+ self.mean = self.weights @ best_solutions
+
+ y_w = (self.mean - old_mean) / self.sigma
+
+ # Step-size control
+ self.ps = (1 - self.cs) * self.ps + np.sqrt(self.cs * (2 - self.cs) * self.mueff) * (self.B @ y_w)
+ self.sigma *= np.exp((self.cs / self.damps) * (np.linalg.norm(self.ps) / self.chiN - 1))
+
+ # Covariance matrix adaptation
+ h_sig = int(np.linalg.norm(self.ps) / np.sqrt(1 - (1 - self.cs)**(2 * self.generations)) / self.chiN < 1.4 + 2 / (self.dim + 1))
+ self.pc = (1 - self.cc) * self.pc + h_sig * np.sqrt(self.cc * (2 - self.cc) * self.mueff) * y_w
+
+ artmp = (best_solutions - old_mean) / self.sigma
+ self.C = (1 - self.c1 - self.cmu) * self.C + self.c1 * (np.outer(self.pc, self.pc) + (1 - h_sig) * self.cc * (2 - self.cc) * self.C) + self.cmu * (artmp.T @ np.diag(self.weights) @ artmp)
+
+ # Decompose C into B and D for sampling in the next generation
+ self.D, self.B = np.linalg.eigh(self.C)
+ self.D = np.sqrt(np.maximum(self.D, 1e-12)) # Add small constant to avoid sqrt of negative
+
+def construct_packing():
+ """Constructs a packing of 26 circles using a CMA-ES Memetic Algorithm."""
+ n = 26
+
+ cma_config = {
+ 'generations': 150,
+ 'sigma': 0.1,
+ 'population_size': 20,
+ }
+ ls_config = {
+ 'iterations': 25,
+ 'learning_rate': 0.005,
+ 'wall_strength': 0.2,
+ }
+
+ # Start with a perturbed 5x5 grid + 26th circle in a promising void
+ initial_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k] = [(col + 0.5) * spacing, (row + 0.5) * spacing]
+ k += 1
+ initial_centers[25] = [spacing, spacing]
+ initial_centers += np.random.normal(0, 0.01, size=initial_centers.shape)
+ x0 = np.clip(initial_centers, 0.0, 1.0).flatten()
+
+ packer = CMAESPacker(x_start=x0, sigma=cma_config['sigma'], population_size=cma_config['population_size'])
+ evaluator = FitnessEvaluator(n=n, ls_config=ls_config)
+
+ best_ever_solution = x0
+ best_ever_fitness = evaluator.evaluate(x0)
+
+ for gen in range(cma_config['generations']):
+ solutions = packer.ask()
+ fitnesses = [evaluator.evaluate(s) for s in solutions]
+ packer.tell(fitnesses)
+
+ current_best_idx = np.argmin(fitnesses)
+ if fitnesses[current_best_idx] < best_ever_fitness:
+ best_ever_fitness = fitnesses[current_best_idx]
+ best_ever_solution = solutions[current_best_idx]
+
+ final_centers = np.clip(best_ever_solution.reshape(n, 2), 0.0, 1.0)
+ final_radii = compute_max_radii(final_centers, max_iter=1500)
+
+ return final_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..639813b26fb788c17c10286874fceb9ed6335512
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/original.py
@@ -0,0 +1,218 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm is a crossover, combining the high-level strategy of the best-performing
+parent (multi-start, two-phase simulation, annealed growth pressure) with the
+efficient vectorized force calculations from the inspiration program.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned hyperparameters for a more aggressive main phase "explosion" and robust fine-tuning.
+ iterations = 1500 # Increased iterations for more thorough exploration after stronger forces
+ learning_rate = 0.015 # Increased learning rate for more significant center movements
+ wall_repulsion_strength = 0.35 # Slightly stronger wall repulsion to contain circles during aggressive phase
+ initial_growth_pressure = 1.03 # Significantly higher initial pressure for stronger repulsion ("explosion")
+ final_growth_pressure = 1.0001 # Closer to 1.0 for precise packing at end of main phase
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Candidate initial positions for the 26th circle to optimize corner utilization.
+ candidate_extra_circle_initial_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Add small random perturbation to the 5x5 grid (first 25 circles, re-introduced from successful prior)
+ perturbation_scale = 0.005 # Increased perturbation scale for better symmetry breaking
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 400 # Increased fine-tune iterations for more precise settling after a dynamic main phase
+ fine_tune_learning_rate = 0.001 # Slightly increased fine-tuning LR to settle from potentially further states
+ finetune_growth_pressure_start = 1.0005 # Subtle growth pressure
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tune
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations): # Changed loop variable name for annealing
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure # Use inflated radii for forces
+
+ # --- Vectorized Force Calculation ---
+ # a. Circle-to-circle forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :] # Use inflated radii
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # b. Wall forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + radii) - 1.0
+ overlap_bottom = radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay)
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2736c56d1793139db7cb1119ffca3c5330780f29
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_76/rewrite.txt
@@ -0,0 +1,213 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# Utility function to compute radii, remains a core component
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-9):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+ # Initial radii are limited by the walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ # Iteratively shrink radii that cause overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+class FitnessEvaluator:
+ """Evaluates a solution vector by running local search and computing radii."""
+ def __init__(self, n, ls_config):
+ self.n = n
+ self.ls_iter = ls_config['iterations']
+ self.ls_lr = ls_config['learning_rate']
+ self.wall_strength = ls_config['wall_strength']
+
+ def refine(self, centers):
+ """Applies a few steps of force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ for _ in range(self.ls_iter):
+ # Use a quick radius calculation for local search
+ radii = compute_max_radii(refined_centers, max_iter=100)
+
+ # --- Vectorized Force Calculation (no growth pressure) ---
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.wall_strength * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.wall_strength * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.wall_strength * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.ls_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+ def evaluate(self, solution_vector):
+ """The main fitness evaluation function for a solution vector."""
+ centers = np.clip(solution_vector.reshape(self.n, 2), 0.0, 1.0)
+
+ # Memetic step: apply local search
+ refined_centers = self.refine(centers)
+
+ # Calculate radii for the refined centers
+ radii = compute_max_radii(refined_centers, max_iter=250)
+
+ # CMA-ES minimizes, so we return the negative sum of radii
+ return -np.sum(radii)
+
+class CMAESPacker:
+ """Implements the CMA-ES algorithm for circle packing."""
+ def __init__(self, x_start, sigma, population_size=None):
+ self.dim = len(x_start)
+ # Set population size (lambda)
+ self.popsize = population_size or 4 + int(3 * np.log(self.dim))
+ # Set number of parents (mu)
+ self.mu = self.popsize // 2
+
+ # Initialize weights for recombination
+ weights = np.log(self.mu + 0.5) - np.log(np.arange(1, self.mu + 1))
+ self.weights = weights / np.sum(weights)
+ self.mueff = np.sum(self.weights)**2 / np.sum(self.weights**2)
+
+ # Initialize strategy parameters for adaptation
+ self.mean = x_start.copy()
+ self.sigma = sigma
+ self.pc = np.zeros(self.dim)
+ self.ps = np.zeros(self.dim)
+ self.C = np.eye(self.dim)
+ self.B = np.eye(self.dim)
+ self.D = np.ones(self.dim)
+ self.chiN = self.dim**0.5 * (1 - 1 / (4 * self.dim) + 1 / (21 * self.dim**2))
+ self.generations = 0
+
+ # Learning rates
+ self.cc = (4 + self.mueff / self.dim) / (self.dim + 4 + 2 * self.mueff / self.dim)
+ self.cs = (self.mueff + 2) / (self.dim + self.mueff + 5)
+ self.c1 = 2 / ((self.dim + 1.3)**2 + self.mueff)
+ self.cmu = min(1 - self.c1, 2 * (self.mueff - 2 + 1 / self.mueff) / ((self.dim + 2)**2 + self.mueff))
+ self.damps = 1 + 2 * max(0, np.sqrt((self.mueff - 1) / (self.dim + 1)) - 1) + self.cs
+
+ def ask(self):
+ """Generate a new population of candidate solutions."""
+ self.solutions = []
+ for _ in range(self.popsize):
+ z = np.random.randn(self.dim)
+ y = self.B @ (self.D * z)
+ x = self.mean + self.sigma * y
+ self.solutions.append(x)
+ return self.solutions
+
+ def tell(self, fitnesses):
+ """Update the internal state based on the fitness of the last population."""
+ self.generations += 1
+ sorted_indices = np.argsort(fitnesses)
+
+ old_mean = self.mean.copy()
+ best_solutions = np.array(self.solutions)[sorted_indices[:self.mu]]
+ self.mean = self.weights @ best_solutions
+
+ y_w = (self.mean - old_mean) / self.sigma
+
+ # Step-size control
+ self.ps = (1 - self.cs) * self.ps + np.sqrt(self.cs * (2 - self.cs) * self.mueff) * (self.B @ y_w)
+ self.sigma *= np.exp((self.cs / self.damps) * (np.linalg.norm(self.ps) / self.chiN - 1))
+
+ # Covariance matrix adaptation
+ h_sig = int(np.linalg.norm(self.ps) / np.sqrt(1 - (1 - self.cs)**(2 * self.generations)) / self.chiN < 1.4 + 2 / (self.dim + 1))
+ self.pc = (1 - self.cc) * self.pc + h_sig * np.sqrt(self.cc * (2 - self.cc) * self.mueff) * y_w
+
+ artmp = (best_solutions - old_mean) / self.sigma
+ self.C = (1 - self.c1 - self.cmu) * self.C + self.c1 * (np.outer(self.pc, self.pc) + (1 - h_sig) * self.cc * (2 - self.cc) * self.C) + self.cmu * (artmp.T @ np.diag(self.weights) @ artmp)
+
+ # Decompose C into B and D for sampling in the next generation
+ self.D, self.B = np.linalg.eigh(self.C)
+ self.D = np.sqrt(np.maximum(self.D, 1e-12)) # Add small constant to avoid sqrt of negative
+
+def construct_packing():
+ """Constructs a packing of 26 circles using a CMA-ES Memetic Algorithm."""
+ n = 26
+
+ cma_config = {
+ 'generations': 150,
+ 'sigma': 0.1,
+ 'population_size': 20,
+ }
+ ls_config = {
+ 'iterations': 25,
+ 'learning_rate': 0.005,
+ 'wall_strength': 0.2,
+ }
+
+ # Start with a perturbed 5x5 grid + 26th circle in a promising void
+ initial_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k] = [(col + 0.5) * spacing, (row + 0.5) * spacing]
+ k += 1
+ initial_centers[25] = [spacing, spacing]
+ initial_centers += np.random.normal(0, 0.01, size=initial_centers.shape)
+ x0 = np.clip(initial_centers, 0.0, 1.0).flatten()
+
+ packer = CMAESPacker(x_start=x0, sigma=cma_config['sigma'], population_size=cma_config['population_size'])
+ evaluator = FitnessEvaluator(n=n, ls_config=ls_config)
+
+ best_ever_solution = x0
+ best_ever_fitness = evaluator.evaluate(x0)
+
+ for gen in range(cma_config['generations']):
+ solutions = packer.ask()
+ fitnesses = [evaluator.evaluate(s) for s in solutions]
+ packer.tell(fitnesses)
+
+ current_best_idx = np.argmin(fitnesses)
+ if fitnesses[current_best_idx] < best_ever_fitness:
+ best_ever_fitness = fitnesses[current_best_idx]
+ best_ever_solution = solutions[current_best_idx]
+
+ final_centers = np.clip(best_ever_solution.reshape(n, 2), 0.0, 1.0)
+ final_radii = compute_max_radii(final_centers, max_iter=1500)
+
+ return final_centers, final_radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2eb1f2ae4331c923a42efd8f73678d542c46138f
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..276a1dbfbf9a73be84823cff5185aba979bb3618
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/edit.diff
@@ -0,0 +1,500 @@
+--- a/original.py
++++ b/original.py
+@@ -1,260 +1,275 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+-class ConfigurableHybridPacker:
+- """
+- A class-based packer encapsulating state and logic for a hybrid
+- 'initialize-and-refine' strategy. This object-oriented structure improves
+- maintainability and separates concerns, with hyperparameters managed in a
+- centralized configuration dictionary.
++def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
++ """
++ Compute maximum radii using iterative proportional scaling with a convergence check.
++ """
++ n = centers.shape[0]
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ for _ in range(max_iter):
++ old_radii = radii.copy()
++ updated = False
++ for i in range(n):
++ for j in range(i + 1, n):
++ dist = np.linalg.norm(centers[i] - centers[j])
++ if radii[i] + radii[j] > dist:
++ if dist < 1e-9:
++ radii[i], radii[j] = 0.0, 0.0
++ else:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++
++ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
++ break
++
++ return np.maximum(radii, 0)
++
++
++class HybridLocalSearcher:
++ """
++ A powerful local search refiner based on a two-phase physical simulation.
++ This replaces the simpler force-directed approach.
+ """
+ def __init__(self, n, config):
+- """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+- self.centers = np.zeros((n, 2))
+- self.radii = np.zeros(n)
+-
+- def _compute_radii_for_centers(self, centers_to_eval, iterations):
+- """
+- Computes maximum radii for a given set of centers using iterative proportional scaling.
+- This is a core utility used both during and after the simulation.
+- """
+- radii = np.min([
+- centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+- centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+- ], axis=0)
+-
+- for _ in range(iterations):
+- updated = False
+- for i in range(self.n):
+- for j in range(i + 1, self.n):
+- dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+-
+- if radii[i] + radii[j] > dist:
+- if dist < 1e-9: # Handle co-located centers
+- radii[i], radii[j] = 0.0, 0.0
+- else:
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+- updated = True
+- if not updated:
+- break
+- return np.maximum(radii, 0)
+-
+- def _run_refinement_simulation(self):
+- """
+- Performs a force-directed simulation to refine circle positions.
+- This simulation uses a 'growth pressure' concept to actively
+- encourage circles to expand and fill empty space.
+- """
++
++ def _compute_radii_for_sim(self, centers):
++ """A fast radius computation for use inside the simulation loop."""
++ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
++
++ def refine(self, centers):
++ """Applies a two-phase physical simulation to refine a configuration."""
++ refined_centers = centers.copy()
++
++ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+- # Anneal growth pressure quadratically from high to low.
+- # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+-
+- # 1. Calculate the maximum non-overlapping radii for the current positions.
+- current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+-
+- # 2. Artificially inflate radii to create an expansion 'pressure'.
++ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+-
+- forces = np.zeros_like(self.centers)
+-
+- # 3. Calculate repulsion forces based on the artificial overlaps.
+- # a) Circle-to-circle repulsion (vectorized)
+- # Calculate pairwise differences and distances
+- diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+-
+- # Prevent division by zero for co-located circles
+- dists[dists < 1e-9] = 1e-9
+-
+- # Calculate pairwise radius sums and then overlaps
+- radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+- np.fill_diagonal(overlaps, 0) # No self-repulsion
+-
+- # Calculate force magnitudes and apply them along the normalized difference vectors
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+-
+- # Sum forces for each circle
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b) Wall repulsion (fully vectorized)
+- forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0]) # Left wall
+- forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0) # Right wall
+- forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1]) # Bottom wall
+- forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0) # Top wall
+-
+- # 4. Update center positions with a decaying learning rate for stability.
+- lr = base_lr * (1.0 - (i_iter / iterations))**2
+- self.centers += forces * lr
+-
+- # 5. Enforce hard boundary constraints.
+- self.centers = np.clip(self.centers, 0.0, 1.0)
+-
+- def _run_fine_tuning(self):
+- """
+- Performs a final, short simulation with no growth pressure. This allows
+- the packing to settle and resolve any minor residual overlaps from the
+- main simulation, enabling a more precise final state.
+- """
+- iterations = self.config['fine_tune_iter']
+- lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+- wall_strength = self.config['wall_strength']
+-
+- for _ in range(iterations):
+- # 1. Calculate radii for the current positions. NO growth pressure.
+- current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+-
+- forces = np.zeros_like(self.centers)
+-
+- # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+- # a) Circle-to-circle repulsion (vectorized)
+- diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
++
++ # Vectorized force calculations
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+-
+- # Overlap is based on actual radii.
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ wall_forces = np.zeros_like(refined_centers)
++ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++ lr = base_lr * (1.0 - progress)**2
++ refined_centers += forces * lr
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ # Phase 2: Fine-tuning with minimal growth pressure
++ iterations_ft = self.config['fine_tune_iter']
++ lr_ft = self.config['fine_tune_lr']
++ for _ in range(iterations_ft):
++ current_radii = self._compute_radii_for_sim(refined_centers)
++
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0
++ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+-
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b) Wall repulsion (fully vectorized, based on actual radii)
+- forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+- forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+- forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+- forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+-
+- # 3. Update positions with small learning rate.
+- self.centers += forces * lr
+-
+- # 4. Enforce hard boundary constraints.
+- self.centers = np.clip(self.centers, 0.0, 1.0)
+-
+- def pack(self, initial_centers):
+- """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+- self.centers = initial_centers.copy()
+- self._run_refinement_simulation()
+- self._run_fine_tuning()
+- # Final computation of radii for the refined centers with high precision.
+- self.radii = self._compute_radii_for_centers(self.centers, self.config['final_radius_iter'])
+- return self.centers, self.radii
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ wall_forces = np.zeros_like(refined_centers)
++ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++ refined_centers += forces * lr_ft
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ return refined_centers
++
++
++class MemeticAlgorithm:
++ """Encapsulates the entire Memetic Algorithm for circle packing."""
++ def __init__(self, n, config):
++ self.n = n
++ self.config = config
++ self.population = []
++ self.fitnesses = np.array([])
++ self.best_solution = None
++ self.best_fitness = -1.0
++ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
++
++ def _initialize_population(self):
++ """Initializes a diverse population using strategic grid-based starts and random individuals."""
++ spacing = 1.0 / 5
++ candidate_extra_positions = [
++ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
++ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
++ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
++ ]
++
++ base_centers = np.zeros((self.n - 1, 2))
++ k = 0
++ for j in range(5):
++ for i in range(5):
++ base_centers[k, 0] = (i + 0.5) * spacing
++ base_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
++
++ for i in range(num_strategic_starts):
++ centers = np.zeros((self.n, 2))
++ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
++ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
++ centers[:self.n-1] = perturbed_base_centers
++ centers[self.n-1] = np.array(candidate_extra_positions[i])
++ self.population.append(centers)
++
++ num_random_starts = self.config['population_size'] - len(self.population)
++ for _ in range(num_random_starts):
++ self.population.append(np.random.rand(self.n, 2))
++
++ def _evaluate_population(self):
++ """Calculates fitness for the population and updates the best solution."""
++ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
++ best_idx = np.argmax(self.fitnesses)
++ if self.fitnesses[best_idx] > self.best_fitness:
++ self.best_fitness = self.fitnesses[best_idx]
++ self.best_solution = self.population[best_idx].copy()
++
++ def _select_parent(self):
++ """Selects a parent using tournament selection."""
++ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
++ tourn_fitnesses = self.fitnesses[tourn_indices]
++ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
++ return self.population[winner_idx]
++
++ def _crossover(self, p1, p2):
++ """Performs uniform crossover, taking whole circles from either parent."""
++ child = p1.copy()
++ mask = np.random.rand(self.n) < 0.5
++ child[mask] = p2[mask]
++ return child
++
++ def _mutate(self, individual, strength):
++ """Applies Gaussian mutation and a chance of a strong mutation."""
++ mutated_ind = individual.copy()
++ for i in range(self.n):
++ if np.random.rand() < self.config['mutation_rate']:
++ noise = np.random.normal(0, strength, size=2)
++ mutated_ind[i] += noise
++
++ if np.random.rand() < 0.02:
++ idx_to_reset = np.random.randint(0, self.n)
++ mutated_ind[idx_to_reset] = np.random.rand(2)
++
++ return np.clip(mutated_ind, 0.0, 1.0)
++
++ def run(self):
++ """Executes the full memetic algorithm evolution."""
++ self._initialize_population()
++
++ for gen in range(self.config['generations']):
++ self._evaluate_population()
++
++ new_population = []
++
++ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
++ for idx in elite_indices:
++ new_population.append(self.population[idx].copy())
++
++ mut_strength = self.config['mut_strength_end'] + \
++ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
++ (1.0 - (gen / self.config['generations']))**2.0
++
++ while len(new_population) < self.config['population_size']:
++ p1 = self._select_parent()
++ p2 = self._select_parent()
++
++ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
++ child = self._mutate(child, mut_strength)
++
++ if np.random.rand() < self.config['local_search_prob']:
++ child = self.local_search_refiner.refine(child)
++
++ new_population.append(child)
++
++ self.population = new_population
++
++ self._evaluate_population()
++ return self.best_solution
++
+
+ def construct_packing():
+ """
+- Main function to construct the circle packing. This implementation uses a multi-start
+- strategy with a ConfigurableHybridPacker to explore various initial configurations
+- and select the best one.
++ Constructs a packing of 26 circles using a hybrid Memetic Algorithm.
+ """
+ n = 26
+- # Hyperparameters for the force-directed simulation
+- packer_config = {
+- 'sim_iter': 3000, # Increased iterations for thorough refinement.
+- 'radius_sim_iter': 200, # Iterations for radius calculation during simulation (speed).
+- 'final_radius_iter': 750, # Iterations for final radius calculation (precision).
+- 'learning_rate': 0.015, # Slightly adjusted learning rate.
+- 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+- 'initial_growth_pressure': 1.04, # Increased pressure to better 'unfreeze' the grid.
+- 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
+- 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+- 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+- 'num_perturbation_trials': 4, # Number of random perturbations per candidate start
+- 'perturbation_scale': 0.005, # Scale of initial random perturbation
++ config = {
++ # MA parameters
++ 'population_size': 50,
++ 'generations': 250,
++ 'elite_count': 3,
++ 'tournament_size': 4,
++ 'mutation_rate': 0.35,
++ 'mut_strength_start': 0.1,
++ 'mut_strength_end': 0.001,
++ 'crossover_rate': 0.9,
++ 'initial_perturbation_scale': 0.01,
++
++ # Memetic Parameters
++ 'local_search_prob': 0.1,
++ 'ls_config': {
++ 'sim_iter': 400,
++ 'radius_sim_iter': 100,
++ 'learning_rate': 0.015,
++ 'wall_strength': 0.6,
++ 'initial_growth_pressure': 1.04,
++ 'final_growth_pressure': 1.001,
++ 'fine_tune_iter': 200,
++ 'fine_tune_lr': 0.0015,
++ }
+ }
+
+- # Initial 5x5 grid setup for 25 circles
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- base_centers = np.zeros((n - 1, 2)) # 25 circles
+- k = 0
+- for j in range(num_cells_side):
+- for i in range(num_cells_side):
+- base_centers[k, 0] = (i + 0.5) * spacing
+- base_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- # Strategic candidate placements for the 26th circle.
+- # Includes interstitial points, corner-offset points, and central locations.
+- candidate_extra_positions = [
+- [spacing, spacing], # (0.2, 0.2) - primary interstitial
+- [spacing, 1 - spacing], # (0.2, 0.8)
+- [1 - spacing, spacing], # (0.8, 0.2)
+- [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- [0.5, 0.5], # Center of the square
+- [spacing, 0.5], # Mid-edge interstitial
+- [0.5, spacing],
+- [1 - spacing, 0.5],
+- [0.5, 1 - spacing],
+- [0.05, 0.05], [0.05, 0.95], # Near corners
+- [0.95, 0.05], [0.95, 0.95],
+- [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Slightly more away from exact corner
+- [0.2, 0.4], [0.4, 0.2] # Additional interstitial points
+- ]
+-
+- best_sum_radii = -1.0
+- best_centers = None
+- best_radii = None
+-
+- # Iterate through candidate positions for the 26th circle, and for each,
+- # run multiple trials with random perturbations to break initial grid symmetry.
+- for extra_pos in candidate_extra_positions:
+- for _ in range(packer_config['num_perturbation_trials']):
+- current_initial_centers = np.zeros((n, 2))
+-
+- # Add random perturbation to the base 5x5 grid to break symmetry
+- perturbation = np.random.normal(0, packer_config['perturbation_scale'], size=base_centers.shape)
+- perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+-
+- current_initial_centers[:n-1] = perturbed_base_centers
+- current_initial_centers[n-1] = np.array(extra_pos)
+-
+- # Create a new packer instance for each stochastic initial configuration
+- packer = ConfigurableHybridPacker(n=n, config=packer_config)
+-
+- centers_candidate, radii_candidate = packer.pack(current_initial_centers)
+- current_sum_radii = np.sum(radii_candidate)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers = centers_candidate.copy()
+- best_radii = radii_candidate.copy()
+-
+- # Fallback if no valid configuration was found (should not happen with comprehensive candidates)
+- if best_centers is None:
+- # Default to a simple initial setup and run if all candidate positions fail unexpectedly
+- default_initial_centers = np.zeros((n, 2))
+- default_initial_centers[:n-1] = base_centers
+- default_initial_centers[n-1] = [0.5, 0.5] # Central placement for 26th circle
+- packer = ConfigurableHybridPacker(n=n, config=packer_config)
+- best_centers, best_radii = packer.pack(default_initial_centers)
+-
+- return best_centers, best_radii
++ solver = MemeticAlgorithm(n=n, config=config)
++ best_centers = solver.run()
++
++ final_radii = compute_max_radii(best_centers, max_iter=2000)
++
++ return best_centers, final_radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9001b32e63ac861c7d70916d3d3d3fa507e5d38
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/main.py
@@ -0,0 +1,275 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This replaces the simpler force-directed approach.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
+ spacing = 1.0 / 5
+ candidate_extra_positions = [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ num_random_starts = self.config['population_size'] - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a hybrid Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # MA parameters
+ 'population_size': 50,
+ 'generations': 250,
+ 'elite_count': 3,
+ 'tournament_size': 4,
+ 'mutation_rate': 0.35,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'initial_perturbation_scale': 0.01,
+
+ # Memetic Parameters
+ 'local_search_prob': 0.1,
+ 'ls_config': {
+ 'sim_iter': 400,
+ 'radius_sim_iter': 100,
+ 'learning_rate': 0.015,
+ 'wall_strength': 0.6,
+ 'initial_growth_pressure': 1.04,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 200,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ final_radii = compute_max_radii(best_centers, max_iter=2000)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d327126c9be6753c5a3ccc47292963a95019cfe8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/original.py
@@ -0,0 +1,260 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(iterations):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0]) # Left wall
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0) # Right wall
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1]) # Bottom wall
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self, initial_centers):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self.centers = initial_centers.copy()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers with high precision.
+ self.radii = self._compute_radii_for_centers(self.centers, self.config['final_radius_iter'])
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation uses a multi-start
+ strategy with a ConfigurableHybridPacker to explore various initial configurations
+ and select the best one.
+ """
+ n = 26
+ # Hyperparameters for the force-directed simulation
+ packer_config = {
+ 'sim_iter': 3000, # Increased iterations for thorough refinement.
+ 'radius_sim_iter': 200, # Iterations for radius calculation during simulation (speed).
+ 'final_radius_iter': 750, # Iterations for final radius calculation (precision).
+ 'learning_rate': 0.015, # Slightly adjusted learning rate.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.04, # Increased pressure to better 'unfreeze' the grid.
+ 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
+ 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ 'num_perturbation_trials': 4, # Number of random perturbations per candidate start
+ 'perturbation_scale': 0.005, # Scale of initial random perturbation
+ }
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points, corner-offset points, and central locations.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2) - primary interstitial
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.5, 0.5], # Center of the square
+ [spacing, 0.5], # Mid-edge interstitial
+ [0.5, spacing],
+ [1 - spacing, 0.5],
+ [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], # Near corners
+ [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Slightly more away from exact corner
+ [0.2, 0.4], [0.4, 0.2] # Additional interstitial points
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle, and for each,
+ # run multiple trials with random perturbations to break initial grid symmetry.
+ for extra_pos in candidate_extra_positions:
+ for _ in range(packer_config['num_perturbation_trials']):
+ current_initial_centers = np.zeros((n, 2))
+
+ # Add random perturbation to the base 5x5 grid to break symmetry
+ perturbation = np.random.normal(0, packer_config['perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+
+ current_initial_centers[:n-1] = perturbed_base_centers
+ current_initial_centers[n-1] = np.array(extra_pos)
+
+ # Create a new packer instance for each stochastic initial configuration
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+
+ centers_candidate, radii_candidate = packer.pack(current_initial_centers)
+ current_sum_radii = np.sum(radii_candidate)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_candidate.copy()
+ best_radii = radii_candidate.copy()
+
+ # Fallback if no valid configuration was found (should not happen with comprehensive candidates)
+ if best_centers is None:
+ # Default to a simple initial setup and run if all candidate positions fail unexpectedly
+ default_initial_centers = np.zeros((n, 2))
+ default_initial_centers[:n-1] = base_centers
+ default_initial_centers[n-1] = [0.5, 0.5] # Central placement for 26th circle
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+ best_centers, best_radii = packer.pack(default_initial_centers)
+
+ return best_centers, best_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..22e7b5e782a7429c65917f417489313cad747835
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results
+Run 1/1 completed in 1472.36 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.3470941351657384
+ public: {'centers_str': ' centers[0] = (0.5664, 0.5934)\n centers[1] = (0.3551, 0.0841)\n centers[2] = (0.5344, 0.0936)\n centers[3] = (0.7391, 0.1058)\n centers[4] = (0.9216, 0.0762)\n centers[5] = (0.0845, 0.3350)\n centers[6] = (0.2078, 0.3524)\n centers[7] = (0.4006, 0.2746)\n centers[8] = (0.6680, 0.3217)\n centers[9] = (0.9017, 0.2495)\n centers[10] = (0.0898, 0.4721)\n centers[11] = (0.2893, 0.4853)\n centers[12] = (0.5014, 0.4861)\n centers[13] = (0.6777, 0.5690)\n centers[14] = (0.8822, 0.4670)\n centers[15] = (0.1239, 0.6855)\n centers[16] = (0.3739, 0.6697)\n centers[17] = (0.5557, 0.6524)\n centers[18] = (0.7037, 0.7409)\n centers[19] = (0.9069, 0.6745)\n centers[20] = (0.0903, 0.9049)\n centers[21] = (0.2871, 0.8908)\n centers[22] = (0.5133, 0.8804)\n centers[23] = (0.7028, 0.9290)\n centers[24] = (0.8868, 0.8877)\n centers[25] = (0.1518, 0.1523)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.3470941351657384}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/packing_viz.png
+ execution_time_mean: 1472.3582342532463
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..b8f5ce78b42e6014b87ce2b653c8f0b11e331dff
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.3470941351657384,
+ "public": {
+ "centers_str": " centers[0] = (0.5664, 0.5934)\n centers[1] = (0.3551, 0.0841)\n centers[2] = (0.5344, 0.0936)\n centers[3] = (0.7391, 0.1058)\n centers[4] = (0.9216, 0.0762)\n centers[5] = (0.0845, 0.3350)\n centers[6] = (0.2078, 0.3524)\n centers[7] = (0.4006, 0.2746)\n centers[8] = (0.6680, 0.3217)\n centers[9] = (0.9017, 0.2495)\n centers[10] = (0.0898, 0.4721)\n centers[11] = (0.2893, 0.4853)\n centers[12] = (0.5014, 0.4861)\n centers[13] = (0.6777, 0.5690)\n centers[14] = (0.8822, 0.4670)\n centers[15] = (0.1239, 0.6855)\n centers[16] = (0.3739, 0.6697)\n centers[17] = (0.5557, 0.6524)\n centers[18] = (0.7037, 0.7409)\n centers[19] = (0.9069, 0.6745)\n centers[20] = (0.0903, 0.9049)\n centers[21] = (0.2871, 0.8908)\n centers[22] = (0.5133, 0.8804)\n centers[23] = (0.7028, 0.9290)\n centers[24] = (0.8868, 0.8877)\n centers[25] = (0.1518, 0.1523)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.3470941351657384
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/results/packing_viz.png",
+ "execution_time_mean": 1472.3582342532463,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c893ec74b71f76c461b968556a38da04ebae957b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_77/rewrite.txt
@@ -0,0 +1,266 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This replaces the simpler force-directed approach.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
+ spacing = 1.0 / 5
+ candidate_extra_positions = [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ num_random_starts = self.config['population_size'] - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a hybrid Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # MA parameters
+ 'population_size': 50,
+ 'generations': 250,
+ 'elite_count': 3,
+ 'tournament_size': 4,
+ 'mutation_rate': 0.35,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'initial_perturbation_scale': 0.01,
+
+ # Memetic Parameters
+ 'local_search_prob': 0.1,
+ 'ls_config': {
+ 'sim_iter': 400,
+ 'radius_sim_iter': 100,
+ 'learning_rate': 0.015,
+ 'wall_strength': 0.6,
+ 'initial_growth_pressure': 1.04,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 200,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ final_radii = compute_max_radii(best_centers, max_iter=2000)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1db850a7e508683656143e3c629e35e983ff65bf
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..d3e3988897e40254031acc6be09d49a9de1e9f2c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/edit.diff
@@ -0,0 +1,327 @@
+--- a/original.py
++++ b/original.py
+@@ -1,288 +1,306 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+- # Interstitial points from 5x5 grid
+- [spacing, spacing], # (0.2, 0.2)
+- [spacing, 0.5], # (0.2, 0.5)
+- [spacing, 1 - spacing], # (0.2, 0.8)
+- [0.5, spacing], # (0.5, 0.2)
+- [0.5, 0.5], # (0.5, 0.5) - Center
+- [0.5, 1 - spacing], # (0.5, 0.8)
+- [1 - spacing, spacing], # (0.8, 0.2)
+- [1 - spacing, 0.5], # (0.8, 0.5)
+- [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- # Corner-offset points
++ # Interstitial points from 5x5 grid (0.2, 0.2) to (0.8, 0.8)
++ [spacing, spacing],
++ [spacing, 0.5],
++ [spacing, 1 - spacing],
++ [0.5, spacing],
++ [0.5, 0.5], # Center
++ [0.5, 1 - spacing],
++ [1 - spacing, spacing],
++ [1 - spacing, 0.5],
++ [1 - spacing, 1 - spacing],
++ # Corner-offset points (closer to edges than interstitial)
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+- [0.95, 0.95]
++ [0.95, 0.95],
++ # Edge midpoints (closer to edges)
++ [0.05, 0.5],
++ [0.5, 0.05],
++ [0.95, 0.5],
++ [0.5, 0.95],
++ # Slightly varied points near existing strategic ones for more diverse starts
++ [spacing * 1.1, spacing * 1.1], # Slightly off (0.2, 0.2)
++ [0.5 - spacing/5, 0.5 + spacing/5], # Slightly off-center
++ [1 - spacing * 1.1, 1 - spacing * 1.1], # Slightly off (0.8, 0.8)
++ [spacing * 0.9, 0.5], # Slightly off (0.2, 0.5)
++ [0.5, spacing * 0.9] # Slightly off (0.5, 0.2)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration and longer refinement.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+- perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
++ perturbation_strength = 0.015 * spacing # Increased perturbation for more initial diversity
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+ def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+- for _ in range(100):
++ convergence_epsilon = 1e-6 # A slightly less strict threshold for simulation
++ max_sim_radii_iterations = 150 # Increased max iterations for robustness
++
++ for _ in range(max_sim_radii_iterations):
+ updated_in_pass = False
++ previous_radii = radii.copy()
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
++ if radii[i] > 0 or radii[j] > 0:
++ radii[i] = 0
++ radii[j] = 0
++ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+- if not updated_in_pass:
++ # Check for convergence: if no updates were made AND the max change is tiny.
++ max_delta_r = np.max(np.abs(radii - previous_radii))
++ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..24a314da3799e108d1610781ae7ab49f5e494927
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/main.py
@@ -0,0 +1,306 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid (0.2, 0.2) to (0.8, 0.8)
+ [spacing, spacing],
+ [spacing, 0.5],
+ [spacing, 1 - spacing],
+ [0.5, spacing],
+ [0.5, 0.5], # Center
+ [0.5, 1 - spacing],
+ [1 - spacing, spacing],
+ [1 - spacing, 0.5],
+ [1 - spacing, 1 - spacing],
+ # Corner-offset points (closer to edges than interstitial)
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95],
+ # Edge midpoints (closer to edges)
+ [0.05, 0.5],
+ [0.5, 0.05],
+ [0.95, 0.5],
+ [0.5, 0.95],
+ # Slightly varied points near existing strategic ones for more diverse starts
+ [spacing * 1.1, spacing * 1.1], # Slightly off (0.2, 0.2)
+ [0.5 - spacing/5, 0.5 + spacing/5], # Slightly off-center
+ [1 - spacing * 1.1, 1 - spacing * 1.1], # Slightly off (0.8, 0.8)
+ [spacing * 0.9, 0.5], # Slightly off (0.2, 0.5)
+ [0.5, spacing * 0.9] # Slightly off (0.5, 0.2)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration and longer refinement.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.015 * spacing # Increased perturbation for more initial diversity
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ convergence_epsilon = 1e-6 # A slightly less strict threshold for simulation
+ max_sim_radii_iterations = 150 # Increased max iterations for robustness
+
+ for _ in range(max_sim_radii_iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae3db02b9c9aa1ea2c02e9d588f23bec25741507
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/original.py
@@ -0,0 +1,288 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration and longer refinement.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..0013e145c54ba9c67a85b61bc9b7a84d9cf8bdb9
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results
+Run 1/1 completed in 134.68 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9789459282671953
+ public: {'centers_str': ' centers[0] = (0.0726, 0.0752)\n centers[1] = (0.3261, 0.0691)\n centers[2] = (0.5030, 0.0997)\n centers[3] = (0.6979, 0.1019)\n centers[4] = (0.8953, 0.1045)\n centers[5] = (0.0696, 0.3325)\n centers[6] = (0.3228, 0.3299)\n centers[7] = (0.5005, 0.3009)\n centers[8] = (0.7008, 0.3003)\n centers[9] = (0.9038, 0.3031)\n centers[10] = (0.1002, 0.5017)\n centers[11] = (0.2989, 0.5025)\n centers[12] = (0.4970, 0.4984)\n centers[13] = (0.7022, 0.4724)\n centers[14] = (0.9014, 0.4998)\n centers[15] = (0.1010, 0.7025)\n centers[16] = (0.2737, 0.6893)\n centers[17] = (0.4565, 0.6780)\n centers[18] = (0.7123, 0.6944)\n centers[19] = (0.9337, 0.6992)\n centers[20] = (0.0881, 0.8963)\n centers[21] = (0.2910, 0.8855)\n centers[22] = (0.5209, 0.8853)\n centers[23] = (0.7093, 0.9249)\n centers[24] = (0.8912, 0.8911)\n centers[25] = (0.2098, 0.2013)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9789459282671953}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/packing_viz.png
+ execution_time_mean: 134.67522416170686
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..b7373aa046824ac26c8543253688ceec5c938583
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9789459282671953,
+ "public": {
+ "centers_str": " centers[0] = (0.0726, 0.0752)\n centers[1] = (0.3261, 0.0691)\n centers[2] = (0.5030, 0.0997)\n centers[3] = (0.6979, 0.1019)\n centers[4] = (0.8953, 0.1045)\n centers[5] = (0.0696, 0.3325)\n centers[6] = (0.3228, 0.3299)\n centers[7] = (0.5005, 0.3009)\n centers[8] = (0.7008, 0.3003)\n centers[9] = (0.9038, 0.3031)\n centers[10] = (0.1002, 0.5017)\n centers[11] = (0.2989, 0.5025)\n centers[12] = (0.4970, 0.4984)\n centers[13] = (0.7022, 0.4724)\n centers[14] = (0.9014, 0.4998)\n centers[15] = (0.1010, 0.7025)\n centers[16] = (0.2737, 0.6893)\n centers[17] = (0.4565, 0.6780)\n centers[18] = (0.7123, 0.6944)\n centers[19] = (0.9337, 0.6992)\n centers[20] = (0.0881, 0.8963)\n centers[21] = (0.2910, 0.8855)\n centers[22] = (0.5209, 0.8853)\n centers[23] = (0.7093, 0.9249)\n centers[24] = (0.8912, 0.8911)\n centers[25] = (0.2098, 0.2013)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9789459282671953
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/results/packing_viz.png",
+ "execution_time_mean": 134.67522416170686,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1d8642607ca17e55b55a6fde27df1d53545fbd8c
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_78/search_replace.txt
@@ -0,0 +1,184 @@
+
+diversify_initial_positions
+
+
+
+This change aims to enhance the search for optimal packing configurations by significantly diversifying the initial placements.
+
+1. **Expanded `candidate_extra_positions`**: The list of initial candidate positions for the 26th circle has been greatly expanded. Instead of just interstitial grid points and direct corner offsets, it now includes mid-edge points and slightly perturbed versions of prominent interstitial locations. This provides a richer set of starting conditions for the force-directed simulation, increasing the likelihood of discovering novel and higher-scoring arrangements.
+
+2. **Increased `perturbation_strength`**: The initial random perturbation applied to the 5x5 grid of 25 circles has been increased from `0.005 * spacing` to `0.015 * spacing`. While still small enough to retain the general grid structure, this stronger perturbation helps to break the initial symmetry more effectively. This allows the subsequent force-directed simulation to explore a wider variety of configurations and escape local optima that a too-rigid initial grid might impose.
+
+
+
+<<<<<<< SEARCH
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration and longer refinement.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+=======
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid (0.2, 0.2) to (0.8, 0.8)
+ [spacing, spacing],
+ [spacing, 0.5],
+ [spacing, 1 - spacing],
+ [0.5, spacing],
+ [0.5, 0.5], # Center
+ [0.5, 1 - spacing],
+ [1 - spacing, spacing],
+ [1 - spacing, 0.5],
+ [1 - spacing, 1 - spacing],
+ # Corner-offset points (closer to edges than interstitial)
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95],
+ # Edge midpoints (closer to edges)
+ [0.05, 0.5],
+ [0.5, 0.05],
+ [0.95, 0.5],
+ [0.5, 0.95],
+ # Slightly varied points near existing strategic ones for more diverse starts
+ [spacing * 1.1, spacing * 1.1], # Slightly off (0.2, 0.2)
+ [0.5 - spacing/5, 0.5 + spacing/5], # Slightly off-center
+ [1 - spacing * 1.1, 1 - spacing * 1.1], # Slightly off (0.8, 0.8)
+ [spacing * 0.9, 0.5], # Slightly off (0.2, 0.5)
+ [0.5, spacing * 0.9] # Slightly off (0.5, 0.2)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration and longer refinement.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.015 * spacing # Increased perturbation for more initial diversity
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+>>>>>>> REPLACE
+
+
+
+adaptive_sim_radii_calc
+
+
+
+The `compute_radii_for_simulation` function previously ran for a fixed 100 iterations, which could be inefficient or insufficient. This change modifies it to be more adaptive and robust:
+
+1. **Adaptive Convergence Check**: A `convergence_epsilon` and an `updated_in_pass` flag have been added. The function now checks if radii have stabilized (no significant updates and change below epsilon) and breaks early if so. This saves computational time when positions are stable.
+2. **Increased Max Iterations**: The maximum number of iterations has been increased from 100 to 150. While the adaptive check will often lead to fewer iterations, having a higher upper limit ensures that if a configuration is particularly complex or unstable, it has enough iterations to reach a reasonable approximation during the simulation. This balances speed with the necessary accuracy for effective force calculation.
+
+
+
+<<<<<<< SEARCH
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+=======
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ convergence_epsilon = 1e-6 # A slightly less strict threshold for simulation
+ max_sim_radii_iterations = 150 # Increased max iterations for robustness
+
+ for _ in range(max_sim_radii_iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0)
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..049972a5d2ea379ed54f48ea254c2ff8b6508fa4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/edit.diff
@@ -0,0 +1,466 @@
+--- a/original.py
++++ b/original.py
+@@ -1,233 +1,306 @@
+ # EVOLVE-BLOCK-START
+ """
+-Physical simulation-based circle packing for n=26 circles.
+-This algorithm iteratively refines circle positions to maximize radii sum.
+-This version refines the annealing strategy and diversifies initial start
+-positions to improve the global search and final packing quality.
++This solution implements a Memetic Algorithm (MA), which combines a Genetic
++Algorithm (GA) for global exploration with a powerful, physics-based local search
++for exploitation. This approach hybridizes the successful population-based
++framework of prior submissions with the refined, two-stage force-directed
++simulation from the current program, which will be repurposed as the local
++search operator.
+ """
+
+ import numpy as np
+
+-def construct_packing():
+- """
+- Constructs a packing of 26 circles by simulating physical repulsion forces
+- to find an optimal arrangement of centers.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- n = 26
+-
+- # --- Hyperparameters for the simulation ---
+- # Tuned parameters for a strong but controlled "explosion" and robust fine-tuning.
+- iterations = 2500 # Further increased iterations for thorough exploration with higher forces.
+- learning_rate = 0.025 # Increased LR for more significant center movements during the main phase.
+- wall_repulsion_strength = 0.5 # Increased wall strength to better contain the more aggressive initial expansion.
+- initial_growth_pressure = 1.06 # Significantly higher initial pressure for a stronger "explosion" phase.
+- final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+-
+- best_sum_radii = -1.0
+- best_final_centers = None
+-
+- # Structured initial positions for the 26th circle, assuming a 5x5 grid for the first 25.
+- # These configurations aim to leverage known patterns and boundary effects.
+- structured_extra_circle_positions = [
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+- [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial voids (corner-like)
+- [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6], # Interstitial voids (central)
+- ]
+- num_random_initial_trials = 4 # Number of trials starting with completely random circle positions
+-
+- # Combine structured and random initializations
+- for trial_idx in range(len(structured_extra_circle_positions) + num_random_initial_trials):
+- current_centers = np.zeros((n, 2))
+-
+- if trial_idx < len(structured_extra_circle_positions):
+- # Structured start: 5x5 grid for 25 circles, plus a specific 26th circle
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- current_centers[k, 0] = (i + 0.5) * spacing
+- current_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+- perturbation_scale = 0.008
+- current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+- current_centers[25] = structured_extra_circle_positions[trial_idx]
+- else:
+- # Fully random start for all circles to explore entirely different configurations
+- current_centers = np.random.rand(n, 2)
+-
+- # Ensure all centers stay within bounds after initial setup and perturbation
+- current_centers = np.clip(current_centers, 0.0, 1.0)
+- centers_for_sim = current_centers.copy()
+-
+- # --- Main Simulation Loop (Vectorized) ---
+- for sim_iter in range(iterations):
+- radii = compute_max_radii(centers_for_sim)
+-
+- # Reverted to QUADRATIC annealing for growth pressure for a fast-subsiding push.
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * \
+- (1.0 - (sim_iter / iterations))**2
+- inflated_radii = radii * current_growth_pressure
+-
+- # --- Vectorized Force Calculation ---
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
++class LocalSearchRefiner:
++ """
++ Performs a fast, two-stage, force-directed local refinement on a set of centers.
++ This is adapted from the full simulation of the parent program to act as a
++ powerful local search operator within the Memetic Algorithm.
++ """
++ def __init__(self, config):
++ self.config = config
++
++ def refine(self, centers):
++ """Applies a two-stage force-directed refinement to polish a solution."""
++ refined_centers = centers.copy()
++ n = refined_centers.shape[0]
++
++ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
++ for sim_iter in range(self.config['lsr_aggressive_iter']):
++ progress = sim_iter / self.config['lsr_aggressive_iter']
++ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
++ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
++ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
++
++ radii = compute_max_radii(refined_centers)
++ inflated_radii = radii * current_pressure
++
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+-
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = inflated_radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+- overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
++ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
++
++ wall_forces = np.zeros_like(refined_centers)
++ wall_strength = self.config['lsr_wall_strength']
++ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+-
+- # --- Update center positions ---
+- current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+- centers_for_sim += forces * current_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # --- Finalization for current trial ---
+- current_final_radii = compute_max_radii(centers_for_sim)
+- current_sum_radii = np.sum(current_final_radii)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_final_centers = centers_for_sim.copy()
+-
+- # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 500 # Increased fine-tune iterations for more precise settling.
+- fine_tune_learning_rate = 0.001 # Kept fine-tuning LR.
+- finetune_growth_pressure_start = 1.0008 # A slightly stronger nudge to escape shallow local minima.
+- finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end.
+- centers_for_sim = best_final_centers.copy()
+-
+- for sim_iter_ft in range(fine_tune_iterations):
+- radii = compute_max_radii(centers_for_sim)
+-
+- # Anneal subtle growth pressure (linear decay for stability).
+- current_finetune_growth_pressure = finetune_growth_pressure_start - \
+- (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+- (sim_iter_ft / fine_tune_iterations)
+- inflated_radii_ft = radii * current_finetune_growth_pressure
+-
+- # --- Vectorized Force Calculation ---
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_radii_pairs - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+- overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces = circle_forces + wall_forces
+-
+- # Anneal learning rate for fine-tuning (linear decay).
+- current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+- centers_for_sim += forces * current_finetune_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- final_centers = centers_for_sim
+- final_radii = compute_max_radii(final_centers)
+-
+- return final_centers, final_radii
++ refined_centers += forces * current_lr
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ # --- Stage 2: Fine-tuning with subtle growth pressure ---
++ for sim_iter_ft in range(self.config['lsr_finetune_iter']):
++ progress = sim_iter_ft / self.config['lsr_finetune_iter']
++ current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
++ current_pressure = self.config['lsr_finetune_pressure_end'] + \
++ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
++
++ radii = compute_max_radii(refined_centers)
++ inflated_radii = radii * current_pressure
++
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++
++ sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
++ overlaps = np.maximum(0, sum_radii_pairs - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
++
++ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
++
++ wall_forces = np.zeros_like(refined_centers)
++ wall_strength = self.config['lsr_wall_strength']
++ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++ refined_centers += forces * current_lr
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ return refined_centers
++
++class MemeticAlgorithm:
++ """Encapsulates the entire Memetic Algorithm for circle packing."""
++ def __init__(self, n, config):
++ self.n = n
++ self.config = config
++ self.population = []
++ self.fitnesses = np.array([])
++ self.best_solution = None
++ self.best_fitness = -1.0
++ self.local_search_refiner = LocalSearchRefiner(config=config)
++
++ def _initialize_population(self):
++ """Initializes a diverse population with both random and grid-based individuals."""
++ num_grid_based = self.config['population_size'] // 3
++
++ for i in range(self.config['population_size']):
++ if i < num_grid_based:
++ centers = np.zeros((self.n, 2))
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for row in range(num_cells_side):
++ for col in range(num_cells_side):
++ centers[k, 0] = (col + 0.5) * spacing
++ centers[k, 1] = (row + 0.5) * spacing
++ k += 1
++ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
++
++ # Place 26th circle in a random-ish interstitial void
++ interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
++ extra_pos = interstitial_points[i % len(interstitial_points)]
++ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
++ self.population.append(np.clip(centers, 0.0, 1.0))
++ else:
++ self.population.append(np.random.rand(self.n, 2))
++
++ def _evaluate_population(self):
++ """Calculates fitness for the population and updates the best solution."""
++ fitnesses = []
++ for ind in self.population:
++ radii = compute_max_radii(ind)
++ fitnesses.append(np.sum(radii))
++ self.fitnesses = np.array(fitnesses)
++
++ best_idx = np.argmax(self.fitnesses)
++ if self.fitnesses[best_idx] > self.best_fitness:
++ self.best_fitness = self.fitnesses[best_idx]
++ self.best_solution = self.population[best_idx].copy()
++
++ def _select_parent(self):
++ """Selects a parent using tournament selection."""
++ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
++ tourn_fitnesses = self.fitnesses[tourn_indices]
++ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
++ return self.population[winner_idx]
++
++ def _crossover(self, p1, p2):
++ """Performs blend crossover (BLX-alpha)."""
++ alpha = self.config['crossover_alpha']
++ child = alpha * p1 + (1.0 - alpha) * p2
++ return np.clip(child, 0.0, 1.0)
++
++ def _mutate(self, individual, strength):
++ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
++ mutated_ind = individual.copy()
++ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
++ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
++ mutated_ind[mutation_mask] += noise
++
++ if np.random.rand() < self.config['jump_mutation_prob']:
++ idx_to_reset = np.random.randint(0, self.n)
++ mutated_ind[idx_to_reset] = np.random.rand(2)
++
++ return np.clip(mutated_ind, 0.0, 1.0)
++
++ def run(self):
++ """Executes the full memetic algorithm evolution."""
++ self._initialize_population()
++
++ for gen in range(self.config['generations']):
++ self._evaluate_population()
++
++ new_population = []
++
++ # Elitism: carry over best individuals
++ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
++ for idx in elite_indices:
++ new_population.append(self.population[idx].copy())
++
++ # Anneal mutation strength
++ mut_strength = self.config['mut_strength_end'] + \
++ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
++ (1.0 - (gen / self.config['generations']))**2.0
++
++ while len(new_population) < self.config['population_size']:
++ p1 = self._select_parent()
++ p2 = self._select_parent()
++
++ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
++ child = self._mutate(child, mut_strength)
++
++ # Memetic step: Apply local search probabilistically
++ if np.random.rand() < self.config['local_search_prob']:
++ child = self.local_search_refiner.refine(child)
++
++ new_population.append(child)
++
++ self.population = new_population
++
++ self._evaluate_population()
++ return self.best_solution
++
++def construct_packing():
++ """
++ Constructs a packing of 26 circles using a Memetic Algorithm.
++ """
++ n = 26
++ config = {
++ # --- GA Parameters ---
++ 'population_size': 80,
++ 'generations': 350,
++ 'elite_count': 4,
++ 'tournament_size': 6,
++ 'mutation_rate': 0.35, # Per-circle mutation probability
++ 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
++ 'mut_strength_start': 0.1,
++ 'mut_strength_end': 0.001,
++ 'crossover_rate': 0.9,
++ 'crossover_alpha': 0.5, # For BLX-alpha crossover
++ # --- Memetic Parameters ---
++ 'local_search_prob': 0.20, # Probability of applying local search to a new child
++ # --- Local Search Refiner (LSR) Parameters ---
++ 'lsr_aggressive_iter': 40,
++ 'lsr_aggressive_lr': 0.025,
++ 'lsr_aggressive_pressure_start': 1.06,
++ 'lsr_aggressive_pressure_end': 1.001,
++ 'lsr_finetune_iter': 80,
++ 'lsr_finetune_lr': 0.001,
++ 'lsr_finetune_pressure_start': 1.0008,
++ 'lsr_finetune_pressure_end': 1.00001,
++ 'lsr_wall_strength': 0.5,
++ }
++
++ solver = MemeticAlgorithm(n=n, config=config)
++ best_centers = solver.run()
++
++ # Final, high-precision radius calculation for the best solution
++ final_radii = compute_max_radii(best_centers)
++
++ return best_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..6d95001722637c0a9df674b7dd049669085ab9e8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/main.py
@@ -0,0 +1,306 @@
+# EVOLVE-BLOCK-START
+"""
+This solution implements a Memetic Algorithm (MA), which combines a Genetic
+Algorithm (GA) for global exploration with a powerful, physics-based local search
+for exploitation. This approach hybridizes the successful population-based
+framework of prior submissions with the refined, two-stage force-directed
+simulation from the current program, which will be repurposed as the local
+search operator.
+"""
+
+import numpy as np
+
+class LocalSearchRefiner:
+ """
+ Performs a fast, two-stage, force-directed local refinement on a set of centers.
+ This is adapted from the full simulation of the parent program to act as a
+ powerful local search operator within the Memetic Algorithm.
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ n = refined_centers.shape[0]
+
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
+ progress = sim_iter / self.config['lsr_aggressive_iter']
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning with subtle growth pressure ---
+ for sim_iter_ft in range(self.config['lsr_finetune_iter']):
+ progress = sim_iter_ft / self.config['lsr_finetune_iter']
+ current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
+ current_pressure = self.config['lsr_finetune_pressure_end'] + \
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 3
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+ # Place 26th circle in a random-ish interstitial void
+ interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ fitnesses = []
+ for ind in self.population:
+ radii = compute_max_radii(ind)
+ fitnesses.append(np.sum(radii))
+ self.fitnesses = np.array(fitnesses)
+
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs blend crossover (BLX-alpha)."""
+ alpha = self.config['crossover_alpha']
+ child = alpha * p1 + (1.0 - alpha) * p2
+ return np.clip(child, 0.0, 1.0)
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over best individuals
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # --- GA Parameters ---
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 4,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.35, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'crossover_alpha': 0.5, # For BLX-alpha crossover
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ # --- Local Search Refiner (LSR) Parameters ---
+ 'lsr_aggressive_iter': 40,
+ 'lsr_aggressive_lr': 0.025,
+ 'lsr_aggressive_pressure_start': 1.06,
+ 'lsr_aggressive_pressure_end': 1.001,
+ 'lsr_finetune_iter': 80,
+ 'lsr_finetune_lr': 0.001,
+ 'lsr_finetune_pressure_start': 1.0008,
+ 'lsr_finetune_pressure_end': 1.00001,
+ 'lsr_wall_strength': 0.5,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers)
+
+ return best_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..25afe75b90a8b15895759a8f164593991fd003fa
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/original.py
@@ -0,0 +1,233 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version refines the annealing strategy and diversifies initial start
+positions to improve the global search and final packing quality.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned parameters for a strong but controlled "explosion" and robust fine-tuning.
+ iterations = 2500 # Further increased iterations for thorough exploration with higher forces.
+ learning_rate = 0.025 # Increased LR for more significant center movements during the main phase.
+ wall_repulsion_strength = 0.5 # Increased wall strength to better contain the more aggressive initial expansion.
+ initial_growth_pressure = 1.06 # Significantly higher initial pressure for a stronger "explosion" phase.
+ final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Structured initial positions for the 26th circle, assuming a 5x5 grid for the first 25.
+ # These configurations aim to leverage known patterns and boundary effects.
+ structured_extra_circle_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial voids (corner-like)
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6], # Interstitial voids (central)
+ ]
+ num_random_initial_trials = 4 # Number of trials starting with completely random circle positions
+
+ # Combine structured and random initializations
+ for trial_idx in range(len(structured_extra_circle_positions) + num_random_initial_trials):
+ current_centers = np.zeros((n, 2))
+
+ if trial_idx < len(structured_extra_circle_positions):
+ # Structured start: 5x5 grid for 25 circles, plus a specific 26th circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+ perturbation_scale = 0.008
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+ current_centers[25] = structured_extra_circle_positions[trial_idx]
+ else:
+ # Fully random start for all circles to explore entirely different configurations
+ current_centers = np.random.rand(n, 2)
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Reverted to QUADRATIC annealing for growth pressure for a fast-subsiding push.
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 500 # Increased fine-tune iterations for more precise settling.
+ fine_tune_learning_rate = 0.001 # Kept fine-tuning LR.
+ finetune_growth_pressure_start = 1.0008 # A slightly stronger nudge to escape shallow local minima.
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end.
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability).
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay).
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ max_radius_iter = 600 # Maximum iterations for radius calculation
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
+
+ for iteration in range(max_radius_iter):
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ radii[i] = 0.0
+ radii[j] = 0.0
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Calculate the maximum absolute change in any radius
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
+
+ # Check for convergence based on max_absolute_change
+ if max_absolute_change < convergence_epsilon:
+ converged_in_a_row += 1
+ if converged_in_a_row >= required_converged_iters:
+ break # Converged for several consecutive iterations
+ else:
+ converged_in_a_row = 0 # Reset counter if change is significant
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..d1569c686a3e12d0927e53c58ac7c5121c7c50ea
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results
+Run 1/1 completed in 4384.68 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.3115550332492028
+ public: {'centers_str': ' centers[0] = (0.0462, 0.0523)\n centers[1] = (0.3764, 0.1151)\n centers[2] = (0.5816, 0.6001)\n centers[3] = (0.7156, 0.1066)\n centers[4] = (0.9111, 0.0904)\n centers[5] = (0.1195, 0.3311)\n centers[6] = (0.3377, 0.3181)\n centers[7] = (0.5095, 0.1846)\n centers[8] = (0.6929, 0.3455)\n centers[9] = (0.9062, 0.2736)\n centers[10] = (0.0893, 0.5390)\n centers[11] = (0.2532, 0.4624)\n centers[12] = (0.4699, 0.4709)\n centers[13] = (0.6600, 0.5538)\n centers[14] = (0.8793, 0.5133)\n centers[15] = (0.0698, 0.7001)\n centers[16] = (0.2929, 0.6885)\n centers[17] = (0.5326, 0.6730)\n centers[18] = (0.7366, 0.7114)\n centers[19] = (0.9294, 0.7037)\n centers[20] = (0.1115, 0.8864)\n centers[21] = (0.2988, 0.9217)\n centers[22] = (0.4946, 0.8782)\n centers[23] = (0.6973, 0.9159)\n centers[24] = (0.8899, 0.8869)\n centers[25] = (0.2116, 0.1417)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.3115550332492028}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/packing_viz.png
+ execution_time_mean: 4384.67602726724
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..ba066bdc28abc39447ddb47fa6d461c672f9d1ad
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.3115550332492028,
+ "public": {
+ "centers_str": " centers[0] = (0.0462, 0.0523)\n centers[1] = (0.3764, 0.1151)\n centers[2] = (0.5816, 0.6001)\n centers[3] = (0.7156, 0.1066)\n centers[4] = (0.9111, 0.0904)\n centers[5] = (0.1195, 0.3311)\n centers[6] = (0.3377, 0.3181)\n centers[7] = (0.5095, 0.1846)\n centers[8] = (0.6929, 0.3455)\n centers[9] = (0.9062, 0.2736)\n centers[10] = (0.0893, 0.5390)\n centers[11] = (0.2532, 0.4624)\n centers[12] = (0.4699, 0.4709)\n centers[13] = (0.6600, 0.5538)\n centers[14] = (0.8793, 0.5133)\n centers[15] = (0.0698, 0.7001)\n centers[16] = (0.2929, 0.6885)\n centers[17] = (0.5326, 0.6730)\n centers[18] = (0.7366, 0.7114)\n centers[19] = (0.9294, 0.7037)\n centers[20] = (0.1115, 0.8864)\n centers[21] = (0.2988, 0.9217)\n centers[22] = (0.4946, 0.8782)\n centers[23] = (0.6973, 0.9159)\n centers[24] = (0.8899, 0.8869)\n centers[25] = (0.2116, 0.1417)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.3115550332492028
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/results/packing_viz.png",
+ "execution_time_mean": 4384.67602726724,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ee9ce55afb7103e7b7a4d9f4930538eac3655753
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_79/search_replace.txt
@@ -0,0 +1,436 @@
+
+memetic_algorithm_with_physics_refiner
+
+
+
+The current program uses a pure physics-based simulation with a multi-start strategy. While effective for local optimization, its performance (score: 1.94) is significantly lower than prior Memetic Algorithm (MA) submissions (score: up to 2.32). This indicates that the pure simulation approach is prone to getting trapped in local optima and lacks the global exploration power needed for this complex problem.
+
+To address this, I am replacing the entire `construct_packing` logic with a full-fledged Memetic Algorithm. This new architecture hybridizes the best of both worlds:
+1. **Global Search (Genetic Algorithm):** A population-based approach using selection (tournament), crossover (blend crossover), and mutation (annealed Gaussian noise + jump mutation) to explore the vast search space and discover promising regions.
+2. **Local Exploitation (Physics Refiner):** The sophisticated two-stage, force-directed simulation from the current program is repurposed into a powerful `LocalSearchRefiner`. This operator is applied probabilistically to new individuals (`local_search_prob`), allowing them to rapidly converge to a nearby local optimum.
+
+By integrating the strong local searcher as a memetic step within a robust GA framework, the algorithm can effectively balance broad exploration with deep exploitation, dramatically increasing its ability to find higher-quality packings and escape the local optima that plagued the pure simulation approach. The parameters for the MA and the local refiner have been carefully chosen to facilitate this synergy.
+
+
+
+<<<<<<< SEARCH
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version refines the annealing strategy and diversifies initial start
+positions to improve the global search and final packing quality.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned parameters for a strong but controlled "explosion" and robust fine-tuning.
+ iterations = 2500 # Further increased iterations for thorough exploration with higher forces.
+ learning_rate = 0.025 # Increased LR for more significant center movements during the main phase.
+ wall_repulsion_strength = 0.5 # Increased wall strength to better contain the more aggressive initial expansion.
+ initial_growth_pressure = 1.06 # Significantly higher initial pressure for a stronger "explosion" phase.
+ final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Structured initial positions for the 26th circle, assuming a 5x5 grid for the first 25.
+ # These configurations aim to leverage known patterns and boundary effects.
+ structured_extra_circle_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial voids (corner-like)
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6], # Interstitial voids (central)
+ ]
+ num_random_initial_trials = 4 # Number of trials starting with completely random circle positions
+
+ # Combine structured and random initializations
+ for trial_idx in range(len(structured_extra_circle_positions) + num_random_initial_trials):
+ current_centers = np.zeros((n, 2))
+
+ if trial_idx < len(structured_extra_circle_positions):
+ # Structured start: 5x5 grid for 25 circles, plus a specific 26th circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+ perturbation_scale = 0.008
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+ current_centers[25] = structured_extra_circle_positions[trial_idx]
+ else:
+ # Fully random start for all circles to explore entirely different configurations
+ current_centers = np.random.rand(n, 2)
+
+ # Ensure all centers stay within bounds after initial setup and perturbation
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Reverted to QUADRATIC annealing for growth pressure for a fast-subsiding push.
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 500 # Increased fine-tune iterations for more precise settling.
+ fine_tune_learning_rate = 0.001 # Kept fine-tuning LR.
+ finetune_growth_pressure_start = 1.0008 # A slightly stronger nudge to escape shallow local minima.
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end.
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability).
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay).
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+=======
+"""
+This solution implements a Memetic Algorithm (MA), which combines a Genetic
+Algorithm (GA) for global exploration with a powerful, physics-based local search
+for exploitation. This approach hybridizes the successful population-based
+framework of prior submissions with the refined, two-stage force-directed
+simulation from the current program, which will be repurposed as the local
+search operator.
+"""
+
+import numpy as np
+
+class LocalSearchRefiner:
+ """
+ Performs a fast, two-stage, force-directed local refinement on a set of centers.
+ This is adapted from the full simulation of the parent program to act as a
+ powerful local search operator within the Memetic Algorithm.
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+ n = refined_centers.shape[0]
+
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
+ progress = sim_iter / self.config['lsr_aggressive_iter']
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning with subtle growth pressure ---
+ for sim_iter_ft in range(self.config['lsr_finetune_iter']):
+ progress = sim_iter_ft / self.config['lsr_finetune_iter']
+ current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
+ current_pressure = self.config['lsr_finetune_pressure_end'] + \
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
+
+ radii = compute_max_radii(refined_centers)
+ inflated_radii = radii * current_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_strength = self.config['lsr_wall_strength']
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * current_lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 3
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+
+ # Place 26th circle in a random-ish interstitial void
+ interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ fitnesses = []
+ for ind in self.population:
+ radii = compute_max_radii(ind)
+ fitnesses.append(np.sum(radii))
+ self.fitnesses = np.array(fitnesses)
+
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs blend crossover (BLX-alpha)."""
+ alpha = self.config['crossover_alpha']
+ child = alpha * p1 + (1.0 - alpha) * p2
+ return np.clip(child, 0.0, 1.0)
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over best individuals
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # --- GA Parameters ---
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 4,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.35, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'crossover_alpha': 0.5, # For BLX-alpha crossover
+ # --- Memetic Parameters ---
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
+ # --- Local Search Refiner (LSR) Parameters ---
+ 'lsr_aggressive_iter': 40,
+ 'lsr_aggressive_lr': 0.025,
+ 'lsr_aggressive_pressure_start': 1.06,
+ 'lsr_aggressive_pressure_end': 1.001,
+ 'lsr_finetune_iter': 80,
+ 'lsr_finetune_lr': 0.001,
+ 'lsr_finetune_pressure_start': 1.0008,
+ 'lsr_finetune_pressure_end': 1.00001,
+ 'lsr_wall_strength': 0.5,
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers)
+
+ return best_centers, final_radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..1bb63c8068900da9fe27d459295495760ec0c491
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/edit.diff
@@ -0,0 +1,141 @@
+--- a/original.py
++++ b/original.py
+@@ -1,128 +1,128 @@
+ # EVOLVE-BLOCK-START
+ """
+ Hybrid circle packing for n=26 circles.
+ Starts with a concentric ring pattern and refines positions using
+ force-directed relaxation, then computes radii iteratively.
+ """
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method, starting from a concentric ring pattern,
+ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with a structured concentric ring pattern.
+ # This provides a symmetric, ordered starting point for the relaxation,
+ # combining the idea from the first parent program.
+ centers = np.zeros((n, 2))
+
+ # Place a circle in the center
+ centers[0] = [0.5, 0.5]
+
+- # Place 8 circles in an inner ring
+- inner_ring_radius = 0.28
+- for i in range(8):
+- angle = 2 * np.pi * i / 8
++ # Place 12 circles in an inner ring for denser core
++ inner_ring_radius = 0.25 # Slightly reduced radius
++ for i in range(12):
++ angle = 2 * np.pi * i / 12
+ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
+
+- # Place the remaining 17 circles in an outer ring
+- outer_ring_radius = 0.48
+- for i in range(17):
+- angle = 2 * np.pi * i / 17
+- centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
++ # Place the remaining 13 circles in an outer ring with more space
++ outer_ring_radius = 0.4 # Significantly reduced radius to give circles more room
++ for i in range(13):
++ angle = 2 * np.pi * i / 13
++ centers[i + 1 + 12] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
+
+ # 2. Iterative Position Refinement (Force Simulation from the second parent)
+- n_iter = 250
++ n_iter = 500 # Increased iterations for better convergence
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..3d55c25b58a181313bf99088638ab194984ec21a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/main.py
@@ -0,0 +1,128 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid circle packing for n=26 circles.
+Starts with a concentric ring pattern and refines positions using
+force-directed relaxation, then computes radii iteratively.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method, starting from a concentric ring pattern,
+ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with a structured concentric ring pattern.
+ # This provides a symmetric, ordered starting point for the relaxation,
+ # combining the idea from the first parent program.
+ centers = np.zeros((n, 2))
+
+ # Place a circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 12 circles in an inner ring for denser core
+ inner_ring_radius = 0.25 # Slightly reduced radius
+ for i in range(12):
+ angle = 2 * np.pi * i / 12
+ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
+
+ # Place the remaining 13 circles in an outer ring with more space
+ outer_ring_radius = 0.4 # Significantly reduced radius to give circles more room
+ for i in range(13):
+ angle = 2 * np.pi * i / 13
+ centers[i + 1 + 12] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
+
+ # 2. Iterative Position Refinement (Force Simulation from the second parent)
+ n_iter = 500 # Increased iterations for better convergence
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d8fb9f1a58532001a828536a4a15a44de7ef0ff
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/original.py
@@ -0,0 +1,128 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid circle packing for n=26 circles.
+Starts with a concentric ring pattern and refines positions using
+force-directed relaxation, then computes radii iteratively.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an arrangement of 26 circles using a force-directed
+ relaxation method, starting from a concentric ring pattern,
+ to maximize the sum of radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+ # Use a fixed seed for reproducibility.
+ np.random.seed(1337)
+
+ # 1. Initial Placement: Start with a structured concentric ring pattern.
+ # This provides a symmetric, ordered starting point for the relaxation,
+ # combining the idea from the first parent program.
+ centers = np.zeros((n, 2))
+
+ # Place a circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles in an inner ring
+ inner_ring_radius = 0.28
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
+
+ # Place the remaining 17 circles in an outer ring
+ outer_ring_radius = 0.48
+ for i in range(17):
+ angle = 2 * np.pi * i / 17
+ centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
+
+ # 2. Iterative Position Refinement (Force Simulation from the second parent)
+ n_iter = 250
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+
+ for _ in range(n_iter):
+ # Vectorized calculation of repulsion forces between all pairs of circles
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dist_sq = np.sum(diffs**2, axis=-1)
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
+ force_repel = np.sum(force_repel_matrix, axis=1)
+
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
+ # This creates a soft potential well keeping circles inside the square.
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
+
+ # Combine forces and update positions
+ total_force = force_repel + wall_strength * force_wall
+ centers += step_size * total_force
+
+ # Enforce hard boundaries
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # 3. Compute optimal radii for the final configuration using the convergent method.
+ radii = compute_max_radii(centers)
+
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for the given centers using an
+ iterative relaxation method until convergence. This is more robust
+ than the original single-pass approach.
+ """
+ n = centers.shape[0]
+
+ # Initialize radii based on the distance to the four walls.
+ radii = np.min([
+ centers[:, 0],
+ centers[:, 1],
+ 1 - centers[:, 0],
+ 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iteratively shrink any overlapping circles until the layout is stable.
+ max_iter = 500 # Safety break for the convergence loop
+ for _ in range(max_iter):
+ changed_in_iter = False
+ # Check every pair of circles for overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ sum_r = radii[i] + radii[j]
+
+ if sum_r > dist:
+ # Overlap detected. Shrink radii proportionally to their size.
+ overlap = sum_r - dist
+ if sum_r > 1e-9: # Avoid division by zero
+ radii[i] -= overlap * (radii[i] / sum_r)
+ radii[j] -= overlap * (radii[j] / sum_r)
+ changed_in_iter = True
+
+ if not changed_in_iter:
+ # If a full pass completes with no changes, the radii have converged.
+ break
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a661a26485abbbd0547164b11c6bdebaf142b628
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_8/search_replace.txt
@@ -0,0 +1,67 @@
+
+increase_n_iter
+
+
+
+The current number of iterations for the force-directed relaxation (`n_iter = 250`) might be insufficient for the system to reach a stable, locally optimal configuration, especially with 26 circles. Increasing the number of iterations allows for more extensive refinement of the circle positions, which can lead to better packing efficiency and a higher sum of radii. This is a low-risk change that generally improves convergence.
+
+
+
+<<<<<<< SEARCH
+ n_iter = 250
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+=======
+ n_iter = 500 # Increased iterations for better convergence
+ step_size = 1e-4
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
+>>>>>>> REPLACE
+
+
+
+
+refine_initial_rings
+
+
+
+The initial placement of circles in concentric rings can significantly influence the final packing. The current configuration (1 central, 8 inner, 17 outer) with `inner_ring_radius = 0.28` and `outer_ring_radius = 0.48` results in a very large central circle and many small circles along the edges, as seen in the visualization. The `outer_ring_radius = 0.48` places circles very close to the unit square's boundaries (at 0.02 or 0.98), immediately limiting their potential radius to 0.02. This constrains the optimization prematurely.
+
+This change aims to:
+1. **Balance circle distribution**: Modify the number of circles in each ring from (1, 8, 17) to (1, 12, 13). This distributes circles more evenly in the inner and middle regions, potentially reducing the dominance of a single central circle and creating a denser core.
+2. **Adjust ring radii**: Decrease `inner_ring_radius` slightly to `0.25` and significantly decrease `outer_ring_radius` to `0.4`. This moves the initial positions of the outer ring circles further from the square's boundaries (e.g., now at `0.5 +/- 0.4 = 0.1` or `0.9`), giving them more space to expand and allowing the force simulation to find better arrangements without immediate boundary constraints. This should lead to a higher sum of radii for these previously tiny circles.
+
+
+
+<<<<<<< SEARCH
+ # Place a circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles in an inner ring
+ inner_ring_radius = 0.28
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
+
+ # Place the remaining 17 circles in an outer ring
+ outer_ring_radius = 0.48
+ for i in range(17):
+ angle = 2 * np.pi * i / 17
+ centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
+=======
+ # Place a circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 12 circles in an inner ring for denser core
+ inner_ring_radius = 0.25 # Slightly reduced radius
+ for i in range(12):
+ angle = 2 * np.pi * i / 12
+ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
+
+ # Place the remaining 13 circles in an outer ring with more space
+ outer_ring_radius = 0.4 # Significantly reduced radius to give circles more room
+ for i in range(13):
+ angle = 2 * np.pi * i / 13
+ centers[i + 1 + 12] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
+>>>>>>> REPLACE
+
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..0685d2176a9bc2813d2ce8a8153f1bde655f4bcb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/edit.diff
@@ -0,0 +1,434 @@
+--- a/original.py
++++ b/original.py
+@@ -1,299 +1,309 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+- # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+- main_iterations = 2500 # Increased iterations for more thorough exploration after vectorization
+- main_learning_rate = 0.015 # Slightly increased initial learning rate for faster initial movements
+- wall_repulsion_strength = 0.4 # Slightly stronger wall repulsion to better contain circles and utilize boundaries
+- initial_growth_pressure = 1.03 # Increased initial growth pressure for more aggressive exploration
+- final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+-
+- fine_tune_iterations = 1000 # Increased fine-tune iterations for more precise settling
+- fine_tune_learning_rate = 0.001 # Slightly increased learning rate for fine-tuning to allow more movement
+- finetune_growth_pressure_start = 1.0008 # Subtle growth pressure for fine-tuning, slightly stronger nudge
+- finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+-
+- # Iterate through candidate positions for the 26th circle to find the best one.
++ # Increased energy and stochasticity to "melt" the initial grid and escape local optima.
++ main_iterations = 2500
++ main_learning_rate = 0.02 # Higher LR for more initial movement
++ wall_repulsion_strength = 0.8 # Stronger walls to better use boundaries
++ initial_growth_pressure = 1.08 # Much higher initial pressure to break grid symmetry
++ final_growth_pressure = 1.0001
++
++ fine_tune_iterations = 1000
++ fine_tune_learning_rate = 0.001
++ finetune_growth_pressure_start = 1.0008
++ finetune_growth_pressure_end = 1.00001
++
++ # Stochastic multi-start parameters
++ num_perturbation_trials = 8 # Run multiple simulations for each candidate position
++ perturbation_scale = 0.01 # Scale of random noise to add to initial grid
++
++ # Iterate through candidate positions for the 26th circle and run multiple perturbed trials for each.
+ for extra_pos in candidate_extra_positions:
+- current_centers = np.zeros((n, 2))
+- current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+- current_centers[25] = np.array(extra_pos)
+-
+- # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+- centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+-
+- for sim_iter in range(main_iterations):
+- radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+-
+- # Anneal growth pressure quadratically
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * \
+- (1.0 - (sim_iter / main_iterations))**2
+- pressured_radii = radii_for_sim * current_growth_pressure
+-
+- forces = np.zeros((n, 2))
+-
+- # a. Vectorized Circle-to-circle repulsion forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_pressured_radii - dists)
+- np.fill_diagonal(overlaps, 0) # No self-repulsion
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- # Avoid division by zero for identical centers, resulting in zero force
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b. Vectorized Wall repulsion forces
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = pressured_radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+- overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces += wall_forces # Add wall forces to total forces
+-
+- # 3. Update center positions based on forces
+- # Annealing learning rate: starts high, decreases over time
+- current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+- centers_for_sim += forces * current_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # --- Post-Simulation Fine-Tuning Phase ---
+- # Continue from the best state of the main simulation for precision.
+- for fine_tune_iter in range(fine_tune_iterations):
+- radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+-
+- # Anneal subtle growth pressure (linear decay)
+- current_finetune_growth_pressure = finetune_growth_pressure_start - \
+- (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+- (fine_tune_iter / fine_tune_iterations)
+- pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+-
+- forces_ft = np.zeros((n, 2))
+-
+- # a. Vectorized Circle-to-circle repulsion forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- forces_ft += np.sum(force_matrix, axis=1)
+-
+- # b. Vectorized Wall repulsion forces
+- wall_forces_ft = np.zeros_like(centers_for_sim)
+- overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+- overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+- overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+- overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+-
+- wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+- wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+- wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+- wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+-
+- forces_ft += wall_forces_ft
+-
+- # Update center positions
+- current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+- centers_for_sim += forces_ft * current_fine_tune_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+- current_radii = compute_max_radii(centers_for_sim)
+- current_sum_radii = np.sum(current_radii)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers = centers_for_sim.copy() # Store the optimized centers
+- best_radii = current_radii.copy()
++ for _ in range(num_perturbation_trials):
++ current_centers = np.zeros((n, 2))
++
++ # Add random perturbation to the base 5x5 grid to break symmetry and "melt" the start
++ perturbation = np.random.normal(0, perturbation_scale, size=base_centers.shape)
++ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
++
++ current_centers[:25] = perturbed_base_centers
++ current_centers[25] = np.array(extra_pos)
++
++ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
++ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
++
++ for sim_iter in range(main_iterations):
++ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
++
++ # Anneal growth pressure quadratically
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * \
++ (1.0 - (sim_iter / main_iterations))**2
++ pressured_radii = radii_for_sim * current_growth_pressure
++
++ forces = np.zeros((n, 2))
++
++ # a. Vectorized Circle-to-circle repulsion forces
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++
++ overlaps = np.maximum(0, sum_pressured_radii - dists)
++ np.fill_diagonal(overlaps, 0) # No self-repulsion
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ # Avoid division by zero for identical centers, resulting in zero force
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
++
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ # b. Vectorized Wall repulsion forces
++ wall_forces = np.zeros_like(centers_for_sim)
++ overlap_left = pressured_radii - centers_for_sim[:, 0]
++ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
++ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
++ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
++
++ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
++ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
++ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
++ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
++
++ forces += wall_forces # Add wall forces to total forces
++
++ # 3. Update center positions based on forces
++ # Annealing learning rate: starts high, decreases over time
++ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
++ centers_for_sim += forces * current_lr
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # --- Post-Simulation Fine-Tuning Phase ---
++ # Continue from the best state of the main simulation for precision.
++ for fine_tune_iter in range(fine_tune_iterations):
++ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
++
++ # Anneal subtle growth pressure (linear decay)
++ current_finetune_growth_pressure = finetune_growth_pressure_start - \
++ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
++ (fine_tune_iter / fine_tune_iterations)
++ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
++
++ forces_ft = np.zeros((n, 2))
++
++ # a. Vectorized Circle-to-circle repulsion forces
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
++
++ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
++ unit_vectors[dists < 1e-9] = 0
++
++ force_matrix = unit_vectors * overlaps[..., np.newaxis]
++ forces_ft += np.sum(force_matrix, axis=1)
++
++ # b. Vectorized Wall repulsion forces
++ wall_forces_ft = np.zeros_like(centers_for_sim)
++ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
++ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
++ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
++ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
++
++ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
++ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
++ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
++ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
++
++ forces_ft += wall_forces_ft
++
++ # Update center positions
++ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
++ centers_for_sim += forces_ft * current_fine_tune_lr
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
++ current_radii = compute_max_radii(centers_for_sim)
++ current_sum_radii = np.sum(current_radii)
++
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_centers = centers_for_sim.copy() # Store the optimized centers
++ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+ def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, leveraging faster force computations.
+ for _ in range(400):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..72549907846f0a8f8c2cee01685609035411074a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/main.py
@@ -0,0 +1,309 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Increased energy and stochasticity to "melt" the initial grid and escape local optima.
+ main_iterations = 2500
+ main_learning_rate = 0.02 # Higher LR for more initial movement
+ wall_repulsion_strength = 0.8 # Stronger walls to better use boundaries
+ initial_growth_pressure = 1.08 # Much higher initial pressure to break grid symmetry
+ final_growth_pressure = 1.0001
+
+ fine_tune_iterations = 1000
+ fine_tune_learning_rate = 0.001
+ finetune_growth_pressure_start = 1.0008
+ finetune_growth_pressure_end = 1.00001
+
+ # Stochastic multi-start parameters
+ num_perturbation_trials = 8 # Run multiple simulations for each candidate position
+ perturbation_scale = 0.01 # Scale of random noise to add to initial grid
+
+ # Iterate through candidate positions for the 26th circle and run multiple perturbed trials for each.
+ for extra_pos in candidate_extra_positions:
+ for _ in range(num_perturbation_trials):
+ current_centers = np.zeros((n, 2))
+
+ # Add random perturbation to the base 5x5 grid to break symmetry and "melt" the start
+ perturbation = np.random.normal(0, perturbation_scale, size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+
+ current_centers[:25] = perturbed_base_centers
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ # Avoid division by zero for identical centers, resulting in zero force
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces += wall_forces # Add wall forces to total forces
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces_ft += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces_ft = np.zeros_like(centers_for_sim)
+ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+
+ forces_ft += wall_forces_ft
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, leveraging faster force computations.
+ for _ in range(400):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5a728fc1478ba88807ac0b74fec60f793dbc2b1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/original.py
@@ -0,0 +1,299 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+ main_iterations = 2500 # Increased iterations for more thorough exploration after vectorization
+ main_learning_rate = 0.015 # Slightly increased initial learning rate for faster initial movements
+ wall_repulsion_strength = 0.4 # Slightly stronger wall repulsion to better contain circles and utilize boundaries
+ initial_growth_pressure = 1.03 # Increased initial growth pressure for more aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+ fine_tune_iterations = 1000 # Increased fine-tune iterations for more precise settling
+ fine_tune_learning_rate = 0.001 # Slightly increased learning rate for fine-tuning to allow more movement
+ finetune_growth_pressure_start = 1.0008 # Subtle growth pressure for fine-tuning, slightly stronger nudge
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ # Avoid division by zero for identical centers, resulting in zero force
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces += wall_forces # Add wall forces to total forces
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces_ft += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces_ft = np.zeros_like(centers_for_sim)
+ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+
+ forces_ft += wall_forces_ft
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, leveraging faster force computations.
+ for _ in range(400):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..b1e6e4dd961c72770f83b1f008749f98d05af909
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results
+Run 1/1 completed in 911.42 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9521225265073627
+ public: {'centers_str': ' centers[0] = (0.0932, 0.0930)\n centers[1] = (0.2760, 0.0754)\n centers[2] = (0.5225, 0.0062)\n centers[3] = (0.7084, 0.0903)\n centers[4] = (0.9113, 0.0853)\n centers[5] = (0.0921, 0.3052)\n centers[6] = (0.2866, 0.3146)\n centers[7] = (0.5391, 0.3974)\n centers[8] = (0.7014, 0.3075)\n centers[9] = (0.8805, 0.2821)\n centers[10] = (0.1103, 0.5054)\n centers[11] = (0.3187, 0.4907)\n centers[12] = (0.4867, 0.4938)\n centers[13] = (0.6623, 0.4268)\n centers[14] = (0.9169, 0.4747)\n centers[15] = (0.0897, 0.6940)\n centers[16] = (0.2297, 0.6599)\n centers[17] = (0.4201, 0.6654)\n centers[18] = (0.7245, 0.6647)\n centers[19] = (0.9500, 0.7392)\n centers[20] = (0.0721, 0.9110)\n centers[21] = (0.2730, 0.8676)\n centers[22] = (0.5205, 0.8870)\n centers[23] = (0.7181, 0.9264)\n centers[24] = (0.8973, 0.8976)\n centers[25] = (0.4795, 0.2022)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9521225265073627}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/results/packing_viz.png
+ execution_time_mean: 911.4166632238775
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1bcc1892d149e739410d3814fb7c28f2aa597d12
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_80/search_replace.txt
@@ -0,0 +1,275 @@
+
+stochastic_grid_melting
+
+
+The current deterministic simulation gets stuck in a local optimum dictated by the initial grid placement, as evidenced by the highly structured but inefficient packing in the visualization. To overcome this, I am introducing a "stochastic grid melting" strategy.
+
+This involves two key changes:
+1. **Stochastic Multi-Start with Perturbation**: Instead of one deterministic run for each candidate position of the 26th circle, the code will now perform `num_perturbation_trials` (set to 8) runs. In each trial, the initial 25 grid points are slightly randomized ("perturbed"). This breaks the rigid grid symmetry from the start, allowing the force-directed simulation to explore a much wider and more diverse set of configurations.
+2. **Increased Simulation "Energy"**: To facilitate the "melting" of the initial grid, I am significantly increasing the forces in the initial phase of the simulation. `initial_growth_pressure` is raised from `1.03` to `1.08`, `wall_repulsion_strength` from `0.4` to `0.8`, and `main_learning_rate` from `0.015` to `0.02`. These stronger forces provide the necessary energy to push circles out of their stable but suboptimal grid positions, enabling the system to discover denser, non-grid-like packings.
+
+This combined approach transforms the algorithm from a simple deterministic relaxation into a more powerful stochastic search method, better equipped to navigate the complex search space and find higher-quality solutions.
+
+
+<<<<<<< SEARCH
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+ main_iterations = 2500 # Increased iterations for more thorough exploration after vectorization
+ main_learning_rate = 0.015 # Slightly increased initial learning rate for faster initial movements
+ wall_repulsion_strength = 0.4 # Slightly stronger wall repulsion to better contain circles and utilize boundaries
+ initial_growth_pressure = 1.03 # Increased initial growth pressure for more aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+ fine_tune_iterations = 1000 # Increased fine-tune iterations for more precise settling
+ fine_tune_learning_rate = 0.001 # Slightly increased learning rate for fine-tuning to allow more movement
+ finetune_growth_pressure_start = 1.0008 # Subtle growth pressure for fine-tuning, slightly stronger nudge
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ # Avoid division by zero for identical centers, resulting in zero force
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces += wall_forces # Add wall forces to total forces
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces_ft += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces_ft = np.zeros_like(centers_for_sim)
+ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+
+ forces_ft += wall_forces_ft
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+=======
+ # --- Simulation Hyperparameters ---
+ # Increased energy and stochasticity to "melt" the initial grid and escape local optima.
+ main_iterations = 2500
+ main_learning_rate = 0.02 # Higher LR for more initial movement
+ wall_repulsion_strength = 0.8 # Stronger walls to better use boundaries
+ initial_growth_pressure = 1.08 # Much higher initial pressure to break grid symmetry
+ final_growth_pressure = 1.0001
+
+ fine_tune_iterations = 1000
+ fine_tune_learning_rate = 0.001
+ finetune_growth_pressure_start = 1.0008
+ finetune_growth_pressure_end = 1.00001
+
+ # Stochastic multi-start parameters
+ num_perturbation_trials = 8 # Run multiple simulations for each candidate position
+ perturbation_scale = 0.01 # Scale of random noise to add to initial grid
+
+ # Iterate through candidate positions for the 26th circle and run multiple perturbed trials for each.
+ for extra_pos in candidate_extra_positions:
+ for _ in range(num_perturbation_trials):
+ current_centers = np.zeros((n, 2))
+
+ # Add random perturbation to the base 5x5 grid to break symmetry and "melt" the start
+ perturbation = np.random.normal(0, perturbation_scale, size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+
+ current_centers[:25] = perturbed_base_centers
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ # Avoid division by zero for identical centers, resulting in zero force
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces += wall_forces # Add wall forces to total forces
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces_ft += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces_ft = np.zeros_like(centers_for_sim)
+ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+
+ forces_ft += wall_forces_ft
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..79b8f9bc42399e712d9d5d2a52fda5789614520d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/edit.diff
@@ -0,0 +1,306 @@
+--- a/original.py
++++ b/original.py
+@@ -1,283 +1,296 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+
+ # --- Stage 1: Aggressive settling with subtle growth pressure ---
+ for i_agg_iter in range(self.config['ls_aggressive_iter']):
+ progress = i_agg_iter / self.config['ls_aggressive_iter']
+ current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+ (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+ (1.0 - progress)**1.5 # Cubic decay for growth pressure
+
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+ pressured_radii = radii * current_growth_pressure
+
+ # Vectorized Force Calculation
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+ (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+ (1.0 - progress)**2.0 # Quadratic decay for LR
+
+ refined_centers += forces * current_lr_agg
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning without growth pressure ---
+ for _ in range(self.config['ls_fine_tune_iter']):
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+
+ # Vectorized Force Calculation (no growth pressure)
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.config['ls_fine_tune_lr']
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+ class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+- interstitial_points = [
++ # A much more diverse set of strategic starting points for the 26th circle.
++ strategic_points = [
++ # Primary interstitial voids of a 5x5 grid
+ [spacing, spacing], [spacing, 1 - spacing],
+- [1 - spacing, spacing], [1 - spacing, 1 - spacing]
++ [1 - spacing, spacing], [1 - spacing, 1 - spacing],
++ # Center of the square
++ [0.5, 0.5],
++ # Mid-edge interstitial voids
++ [spacing, 0.5], [0.5, spacing], [0.8, 0.5], [0.5, 0.8],
++ # Near-corner positions
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
++ # Inner interstitial voids
++ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+- centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+-
+- # Place 26th circle in one of the strategic voids
+- extra_pos = interstitial_points[i % len(interstitial_points)]
+- centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
++
++ # Apply a slightly larger perturbation to break symmetry more effectively
++ perturbation_scale = spacing * 0.025
++ centers[:25, :] += np.random.normal(0, perturbation_scale, size=(25, 2))
++
++ # Place 26th circle in one of the strategic voids, cycling through the list
++ extra_pos = strategic_points[i % len(strategic_points)]
++ centers[25] = np.array(extra_pos) + np.random.normal(0, perturbation_scale, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+
+ current_mutation_rate = self.config['mutation_rate_end'] + \
+ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 400, # Increased generations for more thorough evolution
+ 'elite_count': 4,
+ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+
+ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+
+ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f66da8eee1336f7a362a62167353bcefa39a825
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/main.py
@@ -0,0 +1,296 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+
+ # --- Stage 1: Aggressive settling with subtle growth pressure ---
+ for i_agg_iter in range(self.config['ls_aggressive_iter']):
+ progress = i_agg_iter / self.config['ls_aggressive_iter']
+ current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+ (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+ (1.0 - progress)**1.5 # Cubic decay for growth pressure
+
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+ pressured_radii = radii * current_growth_pressure
+
+ # Vectorized Force Calculation
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+ (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+ (1.0 - progress)**2.0 # Quadratic decay for LR
+
+ refined_centers += forces * current_lr_agg
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning without growth pressure ---
+ for _ in range(self.config['ls_fine_tune_iter']):
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+
+ # Vectorized Force Calculation (no growth pressure)
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.config['ls_fine_tune_lr']
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ # A much more diverse set of strategic starting points for the 26th circle.
+ strategic_points = [
+ # Primary interstitial voids of a 5x5 grid
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ # Center of the square
+ [0.5, 0.5],
+ # Mid-edge interstitial voids
+ [spacing, 0.5], [0.5, spacing], [0.8, 0.5], [0.5, 0.8],
+ # Near-corner positions
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ # Inner interstitial voids
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+
+ # Apply a slightly larger perturbation to break symmetry more effectively
+ perturbation_scale = spacing * 0.025
+ centers[:25, :] += np.random.normal(0, perturbation_scale, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids, cycling through the list
+ extra_pos = strategic_points[i % len(strategic_points)]
+ centers[25] = np.array(extra_pos) + np.random.normal(0, perturbation_scale, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+
+ current_mutation_rate = self.config['mutation_rate_end'] + \
+ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 400, # Increased generations for more thorough evolution
+ 'elite_count': 4,
+ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+
+ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+
+ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..e53e50b4f5ba23507c3bd0b593eab9b77ba8edad
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/original.py
@@ -0,0 +1,283 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+
+ # --- Stage 1: Aggressive settling with subtle growth pressure ---
+ for i_agg_iter in range(self.config['ls_aggressive_iter']):
+ progress = i_agg_iter / self.config['ls_aggressive_iter']
+ current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+ (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+ (1.0 - progress)**1.5 # Cubic decay for growth pressure
+
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+ pressured_radii = radii * current_growth_pressure
+
+ # Vectorized Force Calculation
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+ (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+ (1.0 - progress)**2.0 # Quadratic decay for LR
+
+ refined_centers += forces * current_lr_agg
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning without growth pressure ---
+ for _ in range(self.config['ls_fine_tune_iter']):
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+
+ # Vectorized Force Calculation (no growth pressure)
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.config['ls_fine_tune_lr']
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+
+ current_mutation_rate = self.config['mutation_rate_end'] + \
+ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 400, # Increased generations for more thorough evolution
+ 'elite_count': 4,
+ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+
+ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+
+ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c988af8829311438f7cd6dc6ec45464393c50a87
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_81/search_replace.txt
@@ -0,0 +1,74 @@
+
+diversify_initial_population
+
+
+To improve the global search capability of the memetic algorithm, I am diversifying the initial population. The current implementation seeds a quarter of the population from a 5x5 grid, placing the 26th circle in one of only four interstitial corner voids. This is a very limited set of starting configurations.
+
+My change expands this set of starting positions for the 26th circle to a much more comprehensive list of 17 "strategic points". This list now includes not only the four main interstitial voids but also the center of the square, mid-edge positions, near-corner points, and inner interstitial voids. By providing the GA with a wider and more promising range of initial grid-based solutions, we significantly increase the chances of the evolutionary process finding a path towards a better optimum.
+
+Additionally, I am slightly increasing the random perturbation applied to the initial grid points (from a standard deviation of `0.002` to `0.005`). This helps to more effectively break the initial grid symmetry and provides more varied starting material for the local search refiner and genetic operators to work with, promoting a more thorough exploration of the solution space from the very beginning.
+
+
+<<<<<<< SEARCH
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ interstitial_points = [
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids
+ extra_pos = interstitial_points[i % len(interstitial_points)]
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+=======
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ # A much more diverse set of strategic starting points for the 26th circle.
+ strategic_points = [
+ # Primary interstitial voids of a 5x5 grid
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ # Center of the square
+ [0.5, 0.5],
+ # Mid-edge interstitial voids
+ [spacing, 0.5], [0.5, spacing], [0.8, 0.5], [0.5, 0.8],
+ # Near-corner positions
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ # Inner interstitial voids
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+
+ # Apply a slightly larger perturbation to break symmetry more effectively
+ perturbation_scale = spacing * 0.025
+ centers[:25, :] += np.random.normal(0, perturbation_scale, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids, cycling through the list
+ extra_pos = strategic_points[i % len(strategic_points)]
+ centers[25] = np.array(extra_pos) + np.random.normal(0, perturbation_scale, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..dc8038b6c7e741171e165c8ec3708fd22f98c7d3
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..90e05093813d956928812104421c92dafd995f07
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/edit.diff
@@ -0,0 +1,340 @@
+--- a/original.py
++++ b/original.py
+@@ -1,298 +1,321 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+- # Add a small perturbation to the base grid to break symmetry and encourage rearrangement.
+- perturbation_strength = 0.01 * spacing # 1% of the grid spacing
+-
+- # Iterate through each candidate position for the 26th circle
++ # --- Generate a diverse population of initial configurations ---
++ initial_configurations = []
++
++ # 1. Add configurations based on the 5x5 grid + 26th circle (the original strategy)
++ perturbation_strength_grid = 0.01 * spacing # 1% of the grid spacing
+ for extra_pos in candidate_extra_positions:
+- # Create a new 'centers' array for this trial
+- current_centers_trial = np.zeros((n, 2))
+-
+- # Start with a fresh copy of the base grid and perturb it
+- perturbed_grid = base_25_centers.copy()
+- perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+- current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+-
+- current_centers_trial[n-1] = np.array(extra_pos)
+-
+- # Refine these centers using the simulation, passing all necessary hyperparameters
++ config = np.zeros((n, 2))
++ perturbed_grid = base_25_centers.copy() + np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
++ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
++ config[n-1] = np.array(extra_pos)
++ initial_configurations.append(config)
++
++ # 2. Add purely random configurations to increase diversity
++ num_random_configs = 5
++ for _ in range(num_random_configs):
++ initial_configurations.append(np.random.rand(n, 2))
++
++ # 3. Add configurations based on a different grid structure (e.g., 6x5)
++ num_nonsquare_configs = 4
++ nx, ny = 6, 5
++ spacing_x = 1.0 / nx
++ spacing_y = 1.0 / ny
++ grid_6x5 = np.zeros((nx * ny, 2))
++ k_grid = 0
++ for j in range(ny):
++ for i in range(nx):
++ grid_6x5[k_grid, 0] = (i + 0.5) * spacing_x
++ grid_6x5[k_grid, 1] = (j + 0.5) * spacing_y
++ k_grid += 1
++
++ for _ in range(num_nonsquare_configs):
++ config = grid_6x5[:n, :].copy()
++ # Apply a small perturbation to each of these configs
++ config += np.random.normal(0, 0.015, (n, 2))
++ initial_configurations.append(np.clip(config, 0.0, 1.0))
++
++ # --- Process each initial configuration and find the best result ---
++ for initial_centers in initial_configurations:
++ # Pass a copy of the centers to the refinement function to avoid cross-contamination
+ refined_centers = refine_centers_with_simulation(
+- current_centers_trial,
++ initial_centers.copy(),
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+ def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ ):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+ def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..7732fe45bce2dcf50ae0e471c3ed65d55944143f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/main.py
@@ -0,0 +1,321 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # --- Generate a diverse population of initial configurations ---
+ initial_configurations = []
+
+ # 1. Add configurations based on the 5x5 grid + 26th circle (the original strategy)
+ perturbation_strength_grid = 0.01 * spacing # 1% of the grid spacing
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_grid = base_25_centers.copy() + np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+ config[n-1] = np.array(extra_pos)
+ initial_configurations.append(config)
+
+ # 2. Add purely random configurations to increase diversity
+ num_random_configs = 5
+ for _ in range(num_random_configs):
+ initial_configurations.append(np.random.rand(n, 2))
+
+ # 3. Add configurations based on a different grid structure (e.g., 6x5)
+ num_nonsquare_configs = 4
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5 = np.zeros((nx * ny, 2))
+ k_grid = 0
+ for j in range(ny):
+ for i in range(nx):
+ grid_6x5[k_grid, 0] = (i + 0.5) * spacing_x
+ grid_6x5[k_grid, 1] = (j + 0.5) * spacing_y
+ k_grid += 1
+
+ for _ in range(num_nonsquare_configs):
+ config = grid_6x5[:n, :].copy()
+ # Apply a small perturbation to each of these configs
+ config += np.random.normal(0, 0.015, (n, 2))
+ initial_configurations.append(np.clip(config, 0.0, 1.0))
+
+ # --- Process each initial configuration and find the best result ---
+ for initial_centers in initial_configurations:
+ # Pass a copy of the centers to the refinement function to avoid cross-contamination
+ refined_centers = refine_centers_with_simulation(
+ initial_centers.copy(),
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..37d063913f173f566ad1e59630b3135347d80321
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/original.py
@@ -0,0 +1,298 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # Add a small perturbation to the base grid to break symmetry and encourage rearrangement.
+ perturbation_strength = 0.01 * spacing # 1% of the grid spacing
+
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+
+ # Start with a fresh copy of the base grid and perturb it
+ perturbed_grid = base_25_centers.copy()
+ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..0f0772284b153389881d0b9a8ef73e6b8d8ad797
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results
+Run 1/1 completed in 177.95 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9735080842093156
+ public: {'centers_str': ' centers[0] = (0.0767, 0.0786)\n centers[1] = (0.3248, 0.0749)\n centers[2] = (0.4987, 0.0991)\n centers[3] = (0.7008, 0.0988)\n centers[4] = (0.8935, 0.1005)\n centers[5] = (0.0771, 0.3265)\n centers[6] = (0.3166, 0.3217)\n centers[7] = (0.5001, 0.2997)\n centers[8] = (0.6996, 0.3005)\n centers[9] = (0.9026, 0.2975)\n centers[10] = (0.1025, 0.5018)\n centers[11] = (0.2981, 0.5008)\n centers[12] = (0.4999, 0.4991)\n centers[13] = (0.7003, 0.4701)\n centers[14] = (0.8971, 0.4996)\n centers[15] = (0.0986, 0.6995)\n centers[16] = (0.2730, 0.6831)\n centers[17] = (0.4618, 0.6914)\n centers[18] = (0.7163, 0.6790)\n centers[19] = (0.9310, 0.7009)\n centers[20] = (0.0877, 0.8998)\n centers[21] = (0.2933, 0.8817)\n centers[22] = (0.5151, 0.8965)\n centers[23] = (0.7059, 0.9130)\n centers[24] = (0.8964, 0.8963)\n centers[25] = (0.2084, 0.1963)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9735080842093156}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/packing_viz.png
+ execution_time_mean: 177.9471609890461
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..2dde3ad3859f6fd9d730c8bf269494f74904fea1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9735080842093156,
+ "public": {
+ "centers_str": " centers[0] = (0.0767, 0.0786)\n centers[1] = (0.3248, 0.0749)\n centers[2] = (0.4987, 0.0991)\n centers[3] = (0.7008, 0.0988)\n centers[4] = (0.8935, 0.1005)\n centers[5] = (0.0771, 0.3265)\n centers[6] = (0.3166, 0.3217)\n centers[7] = (0.5001, 0.2997)\n centers[8] = (0.6996, 0.3005)\n centers[9] = (0.9026, 0.2975)\n centers[10] = (0.1025, 0.5018)\n centers[11] = (0.2981, 0.5008)\n centers[12] = (0.4999, 0.4991)\n centers[13] = (0.7003, 0.4701)\n centers[14] = (0.8971, 0.4996)\n centers[15] = (0.0986, 0.6995)\n centers[16] = (0.2730, 0.6831)\n centers[17] = (0.4618, 0.6914)\n centers[18] = (0.7163, 0.6790)\n centers[19] = (0.9310, 0.7009)\n centers[20] = (0.0877, 0.8998)\n centers[21] = (0.2933, 0.8817)\n centers[22] = (0.5151, 0.8965)\n centers[23] = (0.7059, 0.9130)\n centers[24] = (0.8964, 0.8963)\n centers[25] = (0.2084, 0.1963)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9735080842093156
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/results/packing_viz.png",
+ "execution_time_mean": 177.9471609890461,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c574f233120929d39c4ef31bb6303136702e9c32
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_82/search_replace.txt
@@ -0,0 +1,104 @@
+
+diversify_initial_configurations
+
+
+The current approach is a multi-start local search that only explores initial configurations based on a 5x5 grid. This severely limits its ability to find globally optimal packings, which are often not simple grid distortions. The performance of ~1.97 is far from the known best of 2.635, and also significantly lower than a previous genetic algorithm's score of 2.27, which indicates the current method is stuck in a local optimum.
+
+This edit diversifies the set of starting points for the force-directed refinement process, inspired by the population diversity of successful genetic algorithms. Instead of only using perturbed 5x5 grids, I introduce two new types of initial configurations:
+1. **Purely Random Configurations:** `np.random.rand(26, 2)` configurations are added. These have no structural bias and allow the refinement process to explore completely different regions of the solution space.
+2. **Alternative Grid Structures:** Configurations based on a `6x5` grid are introduced. This provides a different starting density and structure compared to the `5x5` grid, encouraging the search for more compact, non-square packings.
+
+By running the powerful two-phase refinement simulation on this more diverse set of initial states (from 13 to 22 configurations), we significantly increase the probability of finding a better packing that is not just a minor variation of the initial 5x5 grid. This change transforms the simple multi-start method into a more robust population-based search strategy.
+
+
+<<<<<<< SEARCH
+ # Add a small perturbation to the base grid to break symmetry and encourage rearrangement.
+ perturbation_strength = 0.01 * spacing # 1% of the grid spacing
+
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+
+ # Start with a fresh copy of the base grid and perturb it
+ perturbed_grid = base_25_centers.copy()
+ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+=======
+ # --- Generate a diverse population of initial configurations ---
+ initial_configurations = []
+
+ # 1. Add configurations based on the 5x5 grid + 26th circle (the original strategy)
+ perturbation_strength_grid = 0.01 * spacing # 1% of the grid spacing
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_grid = base_25_centers.copy() + np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+ config[n-1] = np.array(extra_pos)
+ initial_configurations.append(config)
+
+ # 2. Add purely random configurations to increase diversity
+ num_random_configs = 5
+ for _ in range(num_random_configs):
+ initial_configurations.append(np.random.rand(n, 2))
+
+ # 3. Add configurations based on a different grid structure (e.g., 6x5)
+ num_nonsquare_configs = 4
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5 = np.zeros((nx * ny, 2))
+ k_grid = 0
+ for j in range(ny):
+ for i in range(nx):
+ grid_6x5[k_grid, 0] = (i + 0.5) * spacing_x
+ grid_6x5[k_grid, 1] = (j + 0.5) * spacing_y
+ k_grid += 1
+
+ for _ in range(num_nonsquare_configs):
+ config = grid_6x5[:n, :].copy()
+ # Apply a small perturbation to each of these configs
+ config += np.random.normal(0, 0.015, (n, 2))
+ initial_configurations.append(np.clip(config, 0.0, 1.0))
+
+ # --- Process each initial configuration and find the best result ---
+ for initial_centers in initial_configurations:
+ # Pass a copy of the centers to the refinement function to avoid cross-contamination
+ refined_centers = refine_centers_with_simulation(
+ initial_centers.copy(),
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..a4cfca819d5a3f007e5a5633316e3c2d7dd90b56
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/edit.diff
@@ -0,0 +1,321 @@
+--- a/original.py
++++ b/original.py
+@@ -1,299 +1,306 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+- # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+- main_iterations = 2500 # Increased iterations for more thorough exploration after vectorization
+- main_learning_rate = 0.015 # Slightly increased initial learning rate for faster initial movements
+- wall_repulsion_strength = 0.4 # Slightly stronger wall repulsion to better contain circles and utilize boundaries
+- initial_growth_pressure = 1.03 # Increased initial growth pressure for more aggressive exploration
+- final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+-
+- fine_tune_iterations = 1000 # Increased fine-tune iterations for more precise settling
+- fine_tune_learning_rate = 0.001 # Slightly increased learning rate for fine-tuning to allow more movement
+- finetune_growth_pressure_start = 1.0008 # Subtle growth pressure for fine-tuning, slightly stronger nudge
+- finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
++ # Increased energy and stochasticity to "melt" the initial grid and escape local optima.
++ main_iterations = 2500
++ main_learning_rate = 0.02 # Higher LR for more initial movement
++ wall_repulsion_strength = 0.8 # Stronger walls to better use boundaries
++ initial_growth_pressure = 1.08 # Much higher initial pressure to break grid symmetry
++ final_growth_pressure = 1.0001
++
++ fine_tune_iterations = 1000
++ fine_tune_learning_rate = 0.001
++ finetune_growth_pressure_start = 1.0008
++ finetune_growth_pressure_end = 1.00001
++
++ perturbation_scale = 0.01 # Scale of random noise to add to initial grid
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+- current_centers[:25] = base_centers.copy() # Use copy to ensure independence
++
++ # Add random perturbation to the base 5x5 grid to break symmetry and "melt" the start
++ perturbation = np.random.normal(0, perturbation_scale, size=base_centers.shape)
++ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
++
++ current_centers[:25] = perturbed_base_centers
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ # Avoid division by zero for identical centers, resulting in zero force
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces += wall_forces # Add wall forces to total forces
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces_ft += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces_ft = np.zeros_like(centers_for_sim)
+ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+
+ forces_ft += wall_forces_ft
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+ def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, leveraging faster force computations.
+ for _ in range(400):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a4bf2cdbf9bfa2353a9ccb0df87a76b9044c1d9
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/main.py
@@ -0,0 +1,306 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Increased energy and stochasticity to "melt" the initial grid and escape local optima.
+ main_iterations = 2500
+ main_learning_rate = 0.02 # Higher LR for more initial movement
+ wall_repulsion_strength = 0.8 # Stronger walls to better use boundaries
+ initial_growth_pressure = 1.08 # Much higher initial pressure to break grid symmetry
+ final_growth_pressure = 1.0001
+
+ fine_tune_iterations = 1000
+ fine_tune_learning_rate = 0.001
+ finetune_growth_pressure_start = 1.0008
+ finetune_growth_pressure_end = 1.00001
+
+ perturbation_scale = 0.01 # Scale of random noise to add to initial grid
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+
+ # Add random perturbation to the base 5x5 grid to break symmetry and "melt" the start
+ perturbation = np.random.normal(0, perturbation_scale, size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+
+ current_centers[:25] = perturbed_base_centers
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ # Avoid division by zero for identical centers, resulting in zero force
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces += wall_forces # Add wall forces to total forces
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces_ft += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces_ft = np.zeros_like(centers_for_sim)
+ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+
+ forces_ft += wall_forces_ft
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, leveraging faster force computations.
+ for _ in range(400):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5a728fc1478ba88807ac0b74fec60f793dbc2b1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/original.py
@@ -0,0 +1,299 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+ main_iterations = 2500 # Increased iterations for more thorough exploration after vectorization
+ main_learning_rate = 0.015 # Slightly increased initial learning rate for faster initial movements
+ wall_repulsion_strength = 0.4 # Slightly stronger wall repulsion to better contain circles and utilize boundaries
+ initial_growth_pressure = 1.03 # Increased initial growth pressure for more aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+ fine_tune_iterations = 1000 # Increased fine-tune iterations for more precise settling
+ fine_tune_learning_rate = 0.001 # Slightly increased learning rate for fine-tuning to allow more movement
+ finetune_growth_pressure_start = 1.0008 # Subtle growth pressure for fine-tuning, slightly stronger nudge
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ # Avoid division by zero for identical centers, resulting in zero force
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces += wall_forces # Add wall forces to total forces
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces_ft += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces_ft = np.zeros_like(centers_for_sim)
+ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+
+ forces_ft += wall_forces_ft
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, leveraging faster force computations.
+ for _ in range(400):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2b349f6d21ddb16e02009d054ead035cd5ac0b8d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_83/search_replace.txt
@@ -0,0 +1,66 @@
+The current implementation is a deterministic constructor that starts from a perfect 5x5 grid. The provided visualization and the resulting low score (1.96) clearly show that this method gets stuck in a symmetric, grid-like local optimum. The circles barely move from their initial positions, preventing the formation of a denser, more irregular packing structure which is characteristic of high-performing solutions.
+
+To overcome this, I will reintroduce two critical concepts from previously successful memetic and stochastic algorithms:
+1. **Initial Perturbation**: Instead of starting from a perfect grid, I will apply a small random Gaussian perturbation to the initial positions of the 25 grid-based circles for each simulation run. This "melts" the grid's rigid symmetry, creating a much better starting point for the force-directed simulation and allowing it to explore a wider, more promising region of the solution space.
+2. **Increased Simulation "Energy"**: To capitalize on the perturbed start, I will make the simulation more aggressive by increasing key hyperparameters: `main_learning_rate`, `wall_repulsion_strength`, and `initial_growth_pressure`. This provides stronger forces to push the circles out of their initial arrangement and into a denser packing, effectively breaking free from the grid's influence.
+
+This combined strategy of adding stochasticity to the starting configuration and increasing the simulation's energy is a proven method for escaping poor local optima and discovering significantly better packing arrangements.
+
+
+stochastic_grid_melting
+
+
+The current deterministic grid start gets stuck in a poor, symmetric local optimum. This change introduces two key improvements to break this symmetry and find better solutions:
+
+1. **Initial Perturbation**: A small random Gaussian noise is added to the initial 5x5 grid positions. This "melts" the rigid grid, allowing the simulation to explore more effective, non-grid-like configurations.
+
+2. **Increased Simulation Energy**: The hyperparameters for the main simulation phase (`main_learning_rate`, `wall_repulsion_strength`, `initial_growth_pressure`) are increased. This creates a more aggressive force-directed simulation, providing the necessary energy to push the circles out of their initial arrangement and into a denser packing.
+
+
+<<<<<<< SEARCH
+ # --- Simulation Hyperparameters ---
+ # Tuned based on high-scoring prior programs, incorporating annealing for main and fine-tuning phases.
+ main_iterations = 2500 # Increased iterations for more thorough exploration after vectorization
+ main_learning_rate = 0.015 # Slightly increased initial learning rate for faster initial movements
+ wall_repulsion_strength = 0.4 # Slightly stronger wall repulsion to better contain circles and utilize boundaries
+ initial_growth_pressure = 1.03 # Increased initial growth pressure for more aggressive exploration
+ final_growth_pressure = 1.0001 # Growth pressure at the end of the main phase
+
+ fine_tune_iterations = 1000 # Increased fine-tune iterations for more precise settling
+ fine_tune_learning_rate = 0.001 # Slightly increased learning rate for fine-tuning to allow more movement
+ finetune_growth_pressure_start = 1.0008 # Subtle growth pressure for fine-tuning, slightly stronger nudge
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end of fine-tuning
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+ current_centers[25] = np.array(extra_pos)
+=======
+ # --- Simulation Hyperparameters ---
+ # Increased energy and stochasticity to "melt" the initial grid and escape local optima.
+ main_iterations = 2500
+ main_learning_rate = 0.02 # Higher LR for more initial movement
+ wall_repulsion_strength = 0.8 # Stronger walls to better use boundaries
+ initial_growth_pressure = 1.08 # Much higher initial pressure to break grid symmetry
+ final_growth_pressure = 1.0001
+
+ fine_tune_iterations = 1000
+ fine_tune_learning_rate = 0.001
+ finetune_growth_pressure_start = 1.0008
+ finetune_growth_pressure_end = 1.00001
+
+ perturbation_scale = 0.01 # Scale of random noise to add to initial grid
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+
+ # Add random perturbation to the base 5x5 grid to break symmetry and "melt" the start
+ perturbation = np.random.normal(0, perturbation_scale, size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+
+ current_centers[:25] = perturbed_base_centers
+ current_centers[25] = np.array(extra_pos)
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..f3d2b425a3cf4b401c1956a90337186a8f1a3bc7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/edit.diff
@@ -0,0 +1,328 @@
+--- a/original.py
++++ b/original.py
+@@ -1,321 +1,321 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+- # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
++ # --- Hyperparameters for the simulation phases (hybridized from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+- initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
++ initial_growth_pressure = 1.04 # More aggressive initial pressure from inspiration program.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # --- Generate a diverse population of initial configurations ---
+ initial_configurations = []
+
+- # 1. Add configurations based on the 5x5 grid + 26th circle (the original strategy)
+- perturbation_strength_grid = 0.01 * spacing # 1% of the grid spacing
++ # 1. Add configurations based on the 5x5 grid + 26th circle
++ perturbation_strength_grid = 0.005 * spacing # Smaller perturbation from inspiration program
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_grid = base_25_centers.copy() + np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+ config[n-1] = np.array(extra_pos)
+ initial_configurations.append(config)
+
+ # 2. Add purely random configurations to increase diversity
+ num_random_configs = 5
+ for _ in range(num_random_configs):
+ initial_configurations.append(np.random.rand(n, 2))
+
+ # 3. Add configurations based on a different grid structure (e.g., 6x5)
+ num_nonsquare_configs = 4
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5 = np.zeros((nx * ny, 2))
+ k_grid = 0
+ for j in range(ny):
+ for i in range(nx):
+ grid_6x5[k_grid, 0] = (i + 0.5) * spacing_x
+ grid_6x5[k_grid, 1] = (j + 0.5) * spacing_y
+ k_grid += 1
+
+ for _ in range(num_nonsquare_configs):
+ config = grid_6x5[:n, :].copy()
+ # Apply a small perturbation to each of these configs
+ config += np.random.normal(0, 0.015, (n, 2))
+ initial_configurations.append(np.clip(config, 0.0, 1.0))
+
+ # --- Process each initial configuration and find the best result ---
+ for initial_centers in initial_configurations:
+ # Pass a copy of the centers to the refinement function to avoid cross-contamination
+ refined_centers = refine_centers_with_simulation(
+ initial_centers.copy(),
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+ def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ ):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+ def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a8d6b9d9d9e081c90d6cdbb7948eefc1afe7f2e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/main.py
@@ -0,0 +1,321 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (hybridized from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure from inspiration program.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # --- Generate a diverse population of initial configurations ---
+ initial_configurations = []
+
+ # 1. Add configurations based on the 5x5 grid + 26th circle
+ perturbation_strength_grid = 0.005 * spacing # Smaller perturbation from inspiration program
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_grid = base_25_centers.copy() + np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+ config[n-1] = np.array(extra_pos)
+ initial_configurations.append(config)
+
+ # 2. Add purely random configurations to increase diversity
+ num_random_configs = 5
+ for _ in range(num_random_configs):
+ initial_configurations.append(np.random.rand(n, 2))
+
+ # 3. Add configurations based on a different grid structure (e.g., 6x5)
+ num_nonsquare_configs = 4
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5 = np.zeros((nx * ny, 2))
+ k_grid = 0
+ for j in range(ny):
+ for i in range(nx):
+ grid_6x5[k_grid, 0] = (i + 0.5) * spacing_x
+ grid_6x5[k_grid, 1] = (j + 0.5) * spacing_y
+ k_grid += 1
+
+ for _ in range(num_nonsquare_configs):
+ config = grid_6x5[:n, :].copy()
+ # Apply a small perturbation to each of these configs
+ config += np.random.normal(0, 0.015, (n, 2))
+ initial_configurations.append(np.clip(config, 0.0, 1.0))
+
+ # --- Process each initial configuration and find the best result ---
+ for initial_centers in initial_configurations:
+ # Pass a copy of the centers to the refinement function to avoid cross-contamination
+ refined_centers = refine_centers_with_simulation(
+ initial_centers.copy(),
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..7732fe45bce2dcf50ae0e471c3ed65d55944143f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/original.py
@@ -0,0 +1,321 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # --- Generate a diverse population of initial configurations ---
+ initial_configurations = []
+
+ # 1. Add configurations based on the 5x5 grid + 26th circle (the original strategy)
+ perturbation_strength_grid = 0.01 * spacing # 1% of the grid spacing
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_grid = base_25_centers.copy() + np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+ config[n-1] = np.array(extra_pos)
+ initial_configurations.append(config)
+
+ # 2. Add purely random configurations to increase diversity
+ num_random_configs = 5
+ for _ in range(num_random_configs):
+ initial_configurations.append(np.random.rand(n, 2))
+
+ # 3. Add configurations based on a different grid structure (e.g., 6x5)
+ num_nonsquare_configs = 4
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5 = np.zeros((nx * ny, 2))
+ k_grid = 0
+ for j in range(ny):
+ for i in range(nx):
+ grid_6x5[k_grid, 0] = (i + 0.5) * spacing_x
+ grid_6x5[k_grid, 1] = (j + 0.5) * spacing_y
+ k_grid += 1
+
+ for _ in range(num_nonsquare_configs):
+ config = grid_6x5[:n, :].copy()
+ # Apply a small perturbation to each of these configs
+ config += np.random.normal(0, 0.015, (n, 2))
+ initial_configurations.append(np.clip(config, 0.0, 1.0))
+
+ # --- Process each initial configuration and find the best result ---
+ for initial_centers in initial_configurations:
+ # Pass a copy of the centers to the refinement function to avoid cross-contamination
+ refined_centers = refine_centers_with_simulation(
+ initial_centers.copy(),
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a061b2c8e3493b49da713bab3a14138b39263ddb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_84/rewrite.txt
@@ -0,0 +1,312 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (hybridized from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure from inspiration program.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # --- Generate a diverse population of initial configurations ---
+ initial_configurations = []
+
+ # 1. Add configurations based on the 5x5 grid + 26th circle
+ perturbation_strength_grid = 0.005 * spacing # Smaller perturbation from inspiration program
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_grid = base_25_centers.copy() + np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+ config[n-1] = np.array(extra_pos)
+ initial_configurations.append(config)
+
+ # 2. Add purely random configurations to increase diversity
+ num_random_configs = 5
+ for _ in range(num_random_configs):
+ initial_configurations.append(np.random.rand(n, 2))
+
+ # 3. Add configurations based on a different grid structure (e.g., 6x5)
+ num_nonsquare_configs = 4
+ nx, ny = 6, 5
+ spacing_x = 1.0 / nx
+ spacing_y = 1.0 / ny
+ grid_6x5 = np.zeros((nx * ny, 2))
+ k_grid = 0
+ for j in range(ny):
+ for i in range(nx):
+ grid_6x5[k_grid, 0] = (i + 0.5) * spacing_x
+ grid_6x5[k_grid, 1] = (j + 0.5) * spacing_y
+ k_grid += 1
+
+ for _ in range(num_nonsquare_configs):
+ config = grid_6x5[:n, :].copy()
+ # Apply a small perturbation to each of these configs
+ config += np.random.normal(0, 0.015, (n, 2))
+ initial_configurations.append(np.clip(config, 0.0, 1.0))
+
+ # --- Process each initial configuration and find the best result ---
+ for initial_centers in initial_configurations:
+ # Pass a copy of the centers to the refinement function to avoid cross-contamination
+ refined_centers = refine_centers_with_simulation(
+ initial_centers.copy(),
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..50c324cb771b2bd09ccca86086dd0a3e10006de0
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/edit.diff
@@ -0,0 +1,345 @@
+--- a/original.py
++++ b/original.py
+@@ -1,298 +1,307 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+- initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
++ initial_growth_pressure = 1.04 # Increased pressure to better 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
++ num_trials_per_candidate = 3 # Number of simulations per candidate starting position.
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+- # Includes interstitial points and corner-offset positions to explore diverse optima.
++ # Expanded list to explore a wider range of starting configurations.
+ candidate_extra_positions = [
++ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- [0.05, 0.05], # Corner offsets
++ # Corner-offset points
++ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+- [0.95, 0.95]
++ [0.95, 0.95],
++ # Edge midpoints
++ [0.05, 0.5],
++ [0.5, 0.05],
++ [0.95, 0.5],
++ [0.5, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+- # Add a small perturbation to the base grid to break symmetry and encourage rearrangement.
+- perturbation_strength = 0.01 * spacing # 1% of the grid spacing
+-
+- # Iterate through each candidate position for the 26th circle
++ # Add a stronger perturbation to the base grid to better break symmetry and encourage rearrangement.
++ perturbation_strength = 0.025 * spacing # 2.5% of the grid spacing
++
++ # Iterate through each candidate position, running multiple trials for each to find a better optimum.
+ for extra_pos in candidate_extra_positions:
+- # Create a new 'centers' array for this trial
+- current_centers_trial = np.zeros((n, 2))
+-
+- # Start with a fresh copy of the base grid and perturb it
+- perturbed_grid = base_25_centers.copy()
+- perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+- current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+-
+- current_centers_trial[n-1] = np.array(extra_pos)
+-
+- # Refine these centers using the simulation, passing all necessary hyperparameters
+- refined_centers = refine_centers_with_simulation(
+- current_centers_trial,
+- sim_iter, learning_rate, wall_strength,
+- initial_growth_pressure, final_growth_pressure,
+- fine_tune_iter, fine_tune_lr, radius_sim_iter
+- )
+-
+- # Compute the final, precise radii for this refined configuration
+- current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+- current_sum_radii = np.sum(current_radii_final)
+-
+- # Update if this is the best packing found so far
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers_overall = refined_centers.copy()
+- best_radii_overall = current_radii_final.copy()
++ for _ in range(num_trials_per_candidate):
++ # Create a new 'centers' array for this trial
++ current_centers_trial = np.zeros((n, 2))
++
++ # Start with a fresh copy of the base grid and perturb it
++ perturbed_grid = base_25_centers.copy()
++ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
++ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
++
++ current_centers_trial[n-1] = np.array(extra_pos)
++
++ # Refine these centers using the simulation, passing all necessary hyperparameters
++ refined_centers = refine_centers_with_simulation(
++ current_centers_trial,
++ sim_iter, learning_rate, wall_strength,
++ initial_growth_pressure, final_growth_pressure,
++ fine_tune_iter, fine_tune_lr, radius_sim_iter
++ )
++
++ # Compute the final, precise radii for this refined configuration
++ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
++ current_sum_radii = np.sum(current_radii_final)
++
++ # Update if this is the best packing found so far
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_centers_overall = refined_centers.copy()
++ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+ def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ ):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+ def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..0189b0ccbec58a48884aaafe7997037f915f638e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/main.py
@@ -0,0 +1,307 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.04 # Increased pressure to better 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+ num_trials_per_candidate = 3 # Number of simulations per candidate starting position.
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Expanded list to explore a wider range of starting configurations.
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95],
+ # Edge midpoints
+ [0.05, 0.5],
+ [0.5, 0.05],
+ [0.95, 0.5],
+ [0.5, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # Add a stronger perturbation to the base grid to better break symmetry and encourage rearrangement.
+ perturbation_strength = 0.025 * spacing # 2.5% of the grid spacing
+
+ # Iterate through each candidate position, running multiple trials for each to find a better optimum.
+ for extra_pos in candidate_extra_positions:
+ for _ in range(num_trials_per_candidate):
+ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+
+ # Start with a fresh copy of the base grid and perturb it
+ perturbed_grid = base_25_centers.copy()
+ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..37d063913f173f566ad1e59630b3135347d80321
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/original.py
@@ -0,0 +1,298 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # Add a small perturbation to the base grid to break symmetry and encourage rearrangement.
+ perturbation_strength = 0.01 * spacing # 1% of the grid spacing
+
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+
+ # Start with a fresh copy of the base grid and perturb it
+ perturbed_grid = base_25_centers.copy()
+ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..63eb491470995c0b75a29cc59b6a7057a1e31a86
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_85/search_replace.txt
@@ -0,0 +1,159 @@
+The current approach relies on a small, fixed set of initial configurations based on a perturbed 5x5 grid. The visualization and score suggest the simulation lacks sufficient "energy" to break free from this rigid starting structure and discover a more optimal, non-grid-like packing. The resulting configuration has a low sum of radii and many small, inefficiently packed circles.
+
+To address this, I will implement a multi-pronged strategy to enhance exploration:
+1. **Increase Initial Energy:** I will increase `initial_growth_pressure` and the `perturbation_strength`. This provides a stronger initial "push" and "shake" to the system, helping to shatter the stable grid and encourage a more radical rearrangement of circles into denser configurations.
+2. **Broaden the Search Space:** I will expand the list of candidate starting positions for the 26th circle to include more diverse topologies, such as mid-edge points.
+3. **Implement a Multi-Trial Strategy:** For each candidate starting topology, I will run multiple independent simulations, each with a different random perturbation. This significantly increases the number of unique starting points being refined, improving the probability of finding a high-quality global optimum instead of getting stuck in a poor local one.
+
+These changes transform the algorithm from a simple refiner of a few candidate solutions into a more robust explorer of the solution space.
+
+
+tune_hyperparameters
+
+
+To better break the initial, rigid 5x5 grid structure, I am increasing the "energy" of the system in two ways. First, the `initial_growth_pressure` is raised from 1.03 to 1.04. This creates stronger repulsive forces at the beginning of the simulation, encouraging a more significant rearrangement of the circles. Second, the `perturbation_strength` applied to the initial grid is more than doubled, from `0.01 * spacing` to `0.025 * spacing`. This gives the circles a stronger initial "shake," helping them to break free from their grid-locked positions and explore more diverse configurations during the refinement process.
+
+
+<<<<<<< SEARCH
+ initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
+=======
+ initial_growth_pressure = 1.04 # Increased pressure to better 'unfreeze' the grid.
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ # Add a small perturbation to the base grid to break symmetry and encourage rearrangement.
+ perturbation_strength = 0.01 * spacing # 1% of the grid spacing
+=======
+ # Add a stronger perturbation to the base grid to better break symmetry and encourage rearrangement.
+ perturbation_strength = 0.025 * spacing # 2.5% of the grid spacing
+>>>>>>> REPLACE
+
+
+multi_trial_search
+
+
+The current approach explores a very limited set of 13 starting configurations. To significantly broaden the search for a better packing, this change introduces a multi-trial strategy. A new hyperparameter, `num_trials_per_candidate = 3`, is added. The main loop is then modified to run the full simulation 3 times for each candidate position of the 26th circle, with a new random perturbation applied each time.
+
+Furthermore, the list of `candidate_extra_positions` is expanded to include the four mid-edge points, increasing the diversity of the initial topologies being tested. Combined, these changes increase the total number of simulation runs from 13 to 51 (17 candidates * 3 trials), greatly improving the chances of discovering a higher-quality packing.
+
+
+<<<<<<< SEARCH
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+=======
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+ num_trials_per_candidate = 3 # Number of simulations per candidate starting position.
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+=======
+ # Strategic candidate placements for the 26th circle.
+ # Expanded list to explore a wider range of starting configurations.
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95],
+ # Edge midpoints
+ [0.05, 0.5],
+ [0.5, 0.05],
+ [0.95, 0.5],
+ [0.5, 0.95]
+ ]
+>>>>>>> REPLACE
+
+
+<<<<<<< SEARCH
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+
+ # Start with a fresh copy of the base grid and perturb it
+ perturbed_grid = base_25_centers.copy()
+ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+=======
+ # Iterate through each candidate position, running multiple trials for each to find a better optimum.
+ for extra_pos in candidate_extra_positions:
+ for _ in range(num_trials_per_candidate):
+ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+
+ # Start with a fresh copy of the base grid and perturb it
+ perturbed_grid = base_25_centers.copy()
+ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a0598f664cc9ff27be9fe9a153d7942398dfc480
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..422e8a67ebf322d07458901066ecc5da47dbc61a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/edit.diff
@@ -0,0 +1,329 @@
+--- a/original.py
++++ b/original.py
+@@ -1,306 +1,315 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid (0.2, 0.2) to (0.8, 0.8)
+ [spacing, spacing],
+ [spacing, 0.5],
+ [spacing, 1 - spacing],
+ [0.5, spacing],
+ [0.5, 0.5], # Center
+ [0.5, 1 - spacing],
+ [1 - spacing, spacing],
+ [1 - spacing, 0.5],
+ [1 - spacing, 1 - spacing],
+ # Corner-offset points (closer to edges than interstitial)
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95],
+ # Edge midpoints (closer to edges)
+ [0.05, 0.5],
+ [0.5, 0.05],
+ [0.95, 0.5],
+ [0.5, 0.95],
+ # Slightly varied points near existing strategic ones for more diverse starts
+ [spacing * 1.1, spacing * 1.1], # Slightly off (0.2, 0.2)
+ [0.5 - spacing/5, 0.5 + spacing/5], # Slightly off-center
+ [1 - spacing * 1.1, 1 - spacing * 1.1], # Slightly off (0.8, 0.8)
+ [spacing * 0.9, 0.5], # Slightly off (0.2, 0.5)
+ [0.5, spacing * 0.9] # Slightly off (0.5, 0.2)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration and longer refinement.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+- # Iterate through candidate positions for the 26th circle to find the best one.
++ # --- Generate a diverse set of initial configurations ---
++ initial_configurations = []
++
++ # 1. Add grid-based starts with increased perturbation
++ perturbation_strength = 0.05 * spacing # Larger perturbation to better break grid symmetry
+ for extra_pos in candidate_extra_positions:
+- current_centers = np.zeros((n, 2))
+- current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+-
+- # Add a small random perturbation to the base grid to break symmetry
+- perturbation_strength = 0.015 * spacing # Increased perturbation for more initial diversity
+- current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+-
+- current_centers[25] = np.array(extra_pos)
+-
++ grid_start_centers = np.zeros((n, 2))
++ grid_start_centers[:25] = base_centers.copy()
++ grid_start_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
++ grid_start_centers[25] = np.array(extra_pos)
++ initial_configurations.append(grid_start_centers)
++
++ # 2. Add fully random starts to explore non-grid-like structures
++ num_random_starts = 20 # Add 20 random configurations
++ for _ in range(num_random_starts):
++ random_centers = np.random.rand(n, 2)
++ initial_configurations.append(random_centers)
++
++ # Iterate through all generated initial configurations and run the simulation
++ for initial_centers in initial_configurations:
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+- centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
++ centers_for_sim = np.clip(initial_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+ def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ convergence_epsilon = 1e-6 # A slightly less strict threshold for simulation
+ max_sim_radii_iterations = 150 # Increased max iterations for robustness
+
+ for _ in range(max_sim_radii_iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..02ecae2bdab0cff3b989b7bd7a031a0aa26453cc
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/main.py
@@ -0,0 +1,315 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid (0.2, 0.2) to (0.8, 0.8)
+ [spacing, spacing],
+ [spacing, 0.5],
+ [spacing, 1 - spacing],
+ [0.5, spacing],
+ [0.5, 0.5], # Center
+ [0.5, 1 - spacing],
+ [1 - spacing, spacing],
+ [1 - spacing, 0.5],
+ [1 - spacing, 1 - spacing],
+ # Corner-offset points (closer to edges than interstitial)
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95],
+ # Edge midpoints (closer to edges)
+ [0.05, 0.5],
+ [0.5, 0.05],
+ [0.95, 0.5],
+ [0.5, 0.95],
+ # Slightly varied points near existing strategic ones for more diverse starts
+ [spacing * 1.1, spacing * 1.1], # Slightly off (0.2, 0.2)
+ [0.5 - spacing/5, 0.5 + spacing/5], # Slightly off-center
+ [1 - spacing * 1.1, 1 - spacing * 1.1], # Slightly off (0.8, 0.8)
+ [spacing * 0.9, 0.5], # Slightly off (0.2, 0.5)
+ [0.5, spacing * 0.9] # Slightly off (0.5, 0.2)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration and longer refinement.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # --- Generate a diverse set of initial configurations ---
+ initial_configurations = []
+
+ # 1. Add grid-based starts with increased perturbation
+ perturbation_strength = 0.05 * spacing # Larger perturbation to better break grid symmetry
+ for extra_pos in candidate_extra_positions:
+ grid_start_centers = np.zeros((n, 2))
+ grid_start_centers[:25] = base_centers.copy()
+ grid_start_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+ grid_start_centers[25] = np.array(extra_pos)
+ initial_configurations.append(grid_start_centers)
+
+ # 2. Add fully random starts to explore non-grid-like structures
+ num_random_starts = 20 # Add 20 random configurations
+ for _ in range(num_random_starts):
+ random_centers = np.random.rand(n, 2)
+ initial_configurations.append(random_centers)
+
+ # Iterate through all generated initial configurations and run the simulation
+ for initial_centers in initial_configurations:
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(initial_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ convergence_epsilon = 1e-6 # A slightly less strict threshold for simulation
+ max_sim_radii_iterations = 150 # Increased max iterations for robustness
+
+ for _ in range(max_sim_radii_iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..24a314da3799e108d1610781ae7ab49f5e494927
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/original.py
@@ -0,0 +1,306 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid (0.2, 0.2) to (0.8, 0.8)
+ [spacing, spacing],
+ [spacing, 0.5],
+ [spacing, 1 - spacing],
+ [0.5, spacing],
+ [0.5, 0.5], # Center
+ [0.5, 1 - spacing],
+ [1 - spacing, spacing],
+ [1 - spacing, 0.5],
+ [1 - spacing, 1 - spacing],
+ # Corner-offset points (closer to edges than interstitial)
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95],
+ # Edge midpoints (closer to edges)
+ [0.05, 0.5],
+ [0.5, 0.05],
+ [0.95, 0.5],
+ [0.5, 0.95],
+ # Slightly varied points near existing strategic ones for more diverse starts
+ [spacing * 1.1, spacing * 1.1], # Slightly off (0.2, 0.2)
+ [0.5 - spacing/5, 0.5 + spacing/5], # Slightly off-center
+ [1 - spacing * 1.1, 1 - spacing * 1.1], # Slightly off (0.8, 0.8)
+ [spacing * 0.9, 0.5], # Slightly off (0.2, 0.5)
+ [0.5, spacing * 0.9] # Slightly off (0.5, 0.2)
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration and longer refinement.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # More aggressive initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.015 * spacing # Increased perturbation for more initial diversity
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ convergence_epsilon = 1e-6 # A slightly less strict threshold for simulation
+ max_sim_radii_iterations = 150 # Increased max iterations for robustness
+
+ for _ in range(max_sim_radii_iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..2b50662fbf68bd2a9411ca21a2295f45fc5f47c6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/results
+Run 1/1 completed in 266.96 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.955399424463495
+ public: {'centers_str': ' centers[0] = (0.1148, 0.1055)\n centers[1] = (0.2973, 0.1000)\n centers[2] = (0.4892, 0.0681)\n centers[3] = (0.6945, 0.0940)\n centers[4] = (0.8915, 0.1052)\n centers[5] = (0.0967, 0.3067)\n centers[6] = (0.2898, 0.3066)\n centers[7] = (0.4904, 0.3227)\n centers[8] = (0.6985, 0.3054)\n centers[9] = (0.9054, 0.3042)\n centers[10] = (0.1020, 0.4837)\n centers[11] = (0.3049, 0.4923)\n centers[12] = (0.5007, 0.4956)\n centers[13] = (0.6879, 0.4883)\n centers[14] = (0.8942, 0.5042)\n centers[15] = (0.0960, 0.6947)\n centers[16] = (0.2742, 0.7114)\n centers[17] = (0.4557, 0.6776)\n centers[18] = (0.7057, 0.7022)\n centers[19] = (0.9281, 0.6811)\n centers[20] = (0.0945, 0.9046)\n centers[21] = (0.2916, 0.8978)\n centers[22] = (0.5081, 0.8860)\n centers[23] = (0.6916, 0.9267)\n centers[24] = (0.8893, 0.8893)\n centers[25] = (0.5056, 0.1906)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.955399424463495}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/results/packing_viz.png
+ execution_time_mean: 266.9609220880084
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..480e3d6ea3620cb4fccef2e8a4fc994b437b848b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_86/search_replace.txt
@@ -0,0 +1,232 @@
+
+diversify_initial_configurations
+
+
+The current optimization strategy relies on a multi-start local search, but all starting points are minor perturbations of a 5x5 grid. This severely limits the search space and, as the visualization shows, leads to suboptimal, grid-like solutions. The algorithm gets trapped in local optima related to its initial structure.
+
+To overcome this, I am fundamentally diversifying the set of initial configurations. This change introduces two key improvements:
+1. **Increased Perturbation:** The perturbation strength for the grid-based starts is increased from `0.015 * spacing` to `0.05 * spacing`. This provides a stronger initial "push" to break the grid's inherent symmetry and encourages a more dynamic rearrangement of circles during the simulation.
+2. **Randomized Starts:** Crucially, I am adding 20 completely random starting configurations to the pool of candidates. This allows the powerful physics-based simulation to explore entirely different regions of the solution space—regions that are structurally distinct from a grid and may contain much denser, non-obvious packings.
+
+This hybrid initialization strategy combines the refinement of a good heuristic (the perturbed grid) with the broad exploratory power of random search, significantly increasing the chances of finding a globally superior packing by giving the local search better and more varied starting points.
+
+
+<<<<<<< SEARCH
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.015 * spacing # Increased perturbation for more initial diversity
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+=======
+ # --- Generate a diverse set of initial configurations ---
+ initial_configurations = []
+
+ # 1. Add grid-based starts with increased perturbation
+ perturbation_strength = 0.05 * spacing # Larger perturbation to better break grid symmetry
+ for extra_pos in candidate_extra_positions:
+ grid_start_centers = np.zeros((n, 2))
+ grid_start_centers[:25] = base_centers.copy()
+ grid_start_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+ grid_start_centers[25] = np.array(extra_pos)
+ initial_configurations.append(grid_start_centers)
+
+ # 2. Add fully random starts to explore non-grid-like structures
+ num_random_starts = 20 # Add 20 random configurations
+ for _ in range(num_random_starts):
+ random_centers = np.random.rand(n, 2)
+ initial_configurations.append(random_centers)
+
+ # Iterate through all generated initial configurations and run the simulation
+ for initial_centers in initial_configurations:
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(initial_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3965ca2086b01713dd7a809eace31471cd503b29
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..d7a4390470ade8007a0a5024504892a6ca19f9ca
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/edit.diff
@@ -0,0 +1,536 @@
+--- a/original.py
++++ b/original.py
+@@ -1,309 +1,242 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
+-
+ import numpy as np
+
++class HybridSimulatorPacker:
++ """
++ A class-based packer encapsulating state and logic for a hybrid
++ 'initialize-and-refine' strategy using force-directed simulation.
++ Hyperparameters are managed in a centralized configuration dictionary.
++ """
++ def __init__(self, n, config):
++ """Initializes the packer with the number of circles and a config dict."""
++ self.n = n
++ self.config = config
++ self.best_centers_found = None
++ self.best_radii_found = None
++ self.max_sum_radii = -1.0
++
++ def _compute_radii_iterative(self, centers_to_eval, max_iter=750, convergence_threshold=1e-8):
++ """
++ Computes maximum radii for a given set of centers using iterative proportional scaling
++ with a configurable number of iterations and convergence check.
++ """
++ n_eval = centers_to_eval.shape[0]
++ radii = np.min([
++ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
++ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
++ ], axis=0)
++
++ for _ in range(max_iter):
++ old_radii = radii.copy()
++ updated = False
++ for i in range(n_eval):
++ for j in range(i + 1, n_eval):
++ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
++ if radii[i] + radii[j] > dist:
++ if dist < 1e-9: # Handle co-located or nearly co-located centers
++ radii[i], radii[j] = 0.0, 0.0
++ else:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++
++ # Using a simplified convergence check for speed within simulation
++ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
++ break
++
++ return np.maximum(radii, 0)
++
++ def _run_simulation_and_finetune(self, initial_centers):
++ """
++ Performs a two-phase force-directed simulation (main refinement + fine-tuning)
++ on a given initial configuration.
++ """
++ current_centers = initial_centers.copy()
++
++ # Phase 1: Main refinement with annealing growth pressure and learning rate
++ iterations = self.config['sim_iter']
++ base_lr = self.config['learning_rate']
++ wall_strength = self.config['wall_strength']
++ initial_growth_pressure = self.config['initial_growth_pressure']
++ final_growth_pressure = self.config['final_growth_pressure']
++
++ for i_iter in range(iterations):
++ progress = i_iter / iterations
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2 # Quadratic annealing
++
++ current_radii = self._compute_radii_iterative(current_centers, max_iter=self.config['radius_iter_sim'])
++ pressured_radii = current_radii * current_growth_pressure
++
++ # Vectorized force calculations
++ diffs = current_centers[:, np.newaxis, :] - current_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
++
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ # Wall forces
++ wall_forces = np.zeros_like(current_centers)
++ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (current_centers[:, 0] + pressured_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (current_centers[:, 1] + pressured_radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++ lr = base_lr * (1.0 - progress)**2 # Quadratic annealing for LR
++ current_centers += forces * lr
++ current_centers = np.clip(current_centers, 0.0, 1.0)
++
++ # Phase 2: Fine-tuning with subtle growth pressure
++ iterations_ft = self.config['fine_tune_iter']
++ lr_ft = self.config['fine_tune_lr']
++ fine_tune_growth_pressure_start = self.config['fine_tune_growth_pressure_start']
++ fine_tune_growth_pressure_end = self.config['fine_tune_growth_pressure_end']
++
++ for ft_iter in range(iterations_ft):
++ progress_ft = ft_iter / iterations_ft
++ current_ft_growth_pressure = fine_tune_growth_pressure_end + \
++ (fine_tune_growth_pressure_start - fine_tune_growth_pressure_end) * \
++ (1.0 - progress_ft) # Linear annealing for fine-tune growth pressure
++
++ current_radii_ft = self._compute_radii_iterative(current_centers, max_iter=self.config['radius_iter_ft'])
++ pressured_radii_ft = current_radii_ft * current_ft_growth_pressure
++
++ diffs = current_centers[:, np.newaxis, :] - current_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
++ overlaps_ft = np.maximum(0, radii_sums_ft - dists)
++ np.fill_diagonal(overlaps_ft, 0)
++
++ force_matrix_ft = diffs * (overlaps_ft / dists)[:, :, np.newaxis]
++ circle_forces_ft = np.sum(force_matrix_ft, axis=1)
++
++ wall_forces_ft = np.zeros_like(current_centers)
++ wall_forces_ft[:, 0] += wall_strength * np.maximum(0, pressured_radii_ft - current_centers[:, 0])
++ wall_forces_ft[:, 0] -= wall_strength * np.maximum(0, (current_centers[:, 0] + pressured_radii_ft) - 1.0)
++ wall_forces_ft[:, 1] += wall_strength * np.maximum(0, pressured_radii_ft - current_centers[:, 1])
++ wall_forces_ft[:, 1] -= wall_strength * np.maximum(0, (current_centers[:, 1] + pressured_radii_ft) - 1.0)
++
++ forces_ft = circle_forces_ft + wall_forces_ft
++ current_centers += forces_ft * lr_ft # Fixed LR for fine-tuning
++ current_centers = np.clip(current_centers, 0.0, 1.0)
++
++ # After both simulation phases, compute final accurate radii
++ final_radii = self._compute_radii_iterative(current_centers, max_iter=self.config['final_radius_iter'])
++ current_sum_radii = np.sum(final_radii)
++
++ if current_sum_radii > self.max_sum_radii:
++ self.max_sum_radii = current_sum_radii
++ self.best_centers_found = current_centers.copy()
++ self.best_radii_found = final_radii.copy()
++
++ def pack(self):
++ """
++ Executes the multi-start packing pipeline: initialize base grid,
++ iterate through candidate 26th circle positions, and run simulations.
++ """
++ # 1. Initialize 5x5 grid for 25 circles
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ base_centers_25 = np.zeros((25, 2))
++ k = 0
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ base_centers_25[k, 0] = (i + 0.5) * spacing
++ base_centers_25[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ # 2. Define strategic candidate placements for the 26th circle (expanded list)
++ candidate_extra_positions = [
++ # Interstitial points from 5x5 grid (spacing=0.2)
++ [spacing, spacing], # (0.2, 0.2)
++ [spacing, 1 - spacing], # (0.2, 0.8)
++ [1 - spacing, spacing], # (0.8, 0.2)
++ [1 - spacing, 1 - spacing], # (0.8, 0.8)
++ [0.5, 0.5], # (0.5, 0.5) - Center
++ [spacing, 0.5], # (0.2, 0.5)
++ [0.5, spacing], # (0.5, 0.2)
++ [1 - spacing, 0.5], # (0.8, 0.5)
++ [0.5, 1 - spacing], # (0.5, 0.8)
++ # Corner-offset points (closer to edges/corners)
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
++ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
++ ]
++
++ # 3. Iterate through candidate positions and run multiple perturbed trials
++ for extra_pos_base in candidate_extra_positions:
++ for _ in range(self.config['num_perturbation_trials']):
++ current_centers = np.zeros((self.n, 2))
++
++ # Perturb the base 5x5 grid
++ perturbation_25 = np.random.normal(0, self.config['perturbation_scale'], size=base_centers_25.shape)
++ perturbed_base_centers = np.clip(base_centers_25 + perturbation_25, 0.0, 1.0)
++ current_centers[:25] = perturbed_base_centers
++
++ # Perturb the 26th circle's position
++ perturbation_extra = np.random.normal(0, self.config['perturbation_scale'], size=2)
++ current_centers[25] = np.clip(np.array(extra_pos_base) + perturbation_extra, 0.0, 1.0)
++
++ # Run the two-phase simulation for this initial configuration
++ self._run_simulation_and_finetune(current_centers)
++
++ # Fallback if no valid configuration was found (should not happen)
++ if self.best_centers_found is None:
++ # Default to a simple grid if no solution was better than initial -1.0
++ default_centers = np.zeros((self.n, 2))
++ default_centers[:25] = base_centers_25
++ default_centers[25] = [0.5, 0.5]
++ self.best_centers_found = default_centers
++ self.best_radii_found = self._compute_radii_iterative(default_centers, max_iter=self.config['final_radius_iter'])
++
++ return self.best_centers_found, self.best_radii_found
++
+
+ def construct_packing():
+ """
+- Construct a specific arrangement of 26 circles in a unit square
+- that attempts to maximize the sum of their radii.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- # Initialize arrays for 26 circles
++ Constructs a packing of 26 circles using a Hybrid Simulator Packer.
++ """
+ n = 26
+-
+- # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side # spacing is 0.2
+-
+- base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+- k = 0
+- for j in range(num_cells_side):
+- for i in range(num_cells_side):
+- base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+- base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+- k += 1
+-
+- # Define strategic candidate placements for the 26th circle.
+- # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+- candidate_extra_positions = [
+- # Interstitial points from 5x5 grid
+- [spacing, spacing], # (0.2, 0.2)
+- [spacing, 0.5], # (0.2, 0.5)
+- [spacing, 1 - spacing], # (0.2, 0.8)
+- [0.5, spacing], # (0.5, 0.2)
+- [0.5, 0.5], # (0.5, 0.5) - Center
+- [0.5, 1 - spacing], # (0.5, 0.8)
+- [1 - spacing, spacing], # (0.8, 0.2)
+- [1 - spacing, 0.5], # (0.8, 0.5)
+- [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- # Corner-offset points
+- [0.05, 0.05],
+- [0.05, 0.95],
+- [0.95, 0.05],
+- [0.95, 0.95]
+- ]
+-
+- best_sum_radii = 0
+- best_centers = None
+- best_radii = None
+-
+- # --- Simulation Hyperparameters ---
+- # Increased energy and stochasticity to "melt" the initial grid and escape local optima.
+- main_iterations = 2500
+- main_learning_rate = 0.02 # Higher LR for more initial movement
+- wall_repulsion_strength = 0.8 # Stronger walls to better use boundaries
+- initial_growth_pressure = 1.08 # Much higher initial pressure to break grid symmetry
+- final_growth_pressure = 1.0001
+-
+- fine_tune_iterations = 1000
+- fine_tune_learning_rate = 0.001
+- finetune_growth_pressure_start = 1.0008
+- finetune_growth_pressure_end = 1.00001
+-
+- # Stochastic multi-start parameters
+- num_perturbation_trials = 8 # Run multiple simulations for each candidate position
+- perturbation_scale = 0.01 # Scale of random noise to add to initial grid
+-
+- # Iterate through candidate positions for the 26th circle and run multiple perturbed trials for each.
+- for extra_pos in candidate_extra_positions:
+- for _ in range(num_perturbation_trials):
+- current_centers = np.zeros((n, 2))
+-
+- # Add random perturbation to the base 5x5 grid to break symmetry and "melt" the start
+- perturbation = np.random.normal(0, perturbation_scale, size=base_centers.shape)
+- perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+-
+- current_centers[:25] = perturbed_base_centers
+- current_centers[25] = np.array(extra_pos)
+-
+- # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+- centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+-
+- for sim_iter in range(main_iterations):
+- radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+-
+- # Anneal growth pressure quadratically
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * \
+- (1.0 - (sim_iter / main_iterations))**2
+- pressured_radii = radii_for_sim * current_growth_pressure
+-
+- forces = np.zeros((n, 2))
+-
+- # a. Vectorized Circle-to-circle repulsion forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_pressured_radii - dists)
+- np.fill_diagonal(overlaps, 0) # No self-repulsion
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- # Avoid division by zero for identical centers, resulting in zero force
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b. Vectorized Wall repulsion forces
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = pressured_radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+- overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces += wall_forces # Add wall forces to total forces
+-
+- # 3. Update center positions based on forces
+- # Annealing learning rate: starts high, decreases over time
+- current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+- centers_for_sim += forces * current_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # --- Post-Simulation Fine-Tuning Phase ---
+- # Continue from the best state of the main simulation for precision.
+- for fine_tune_iter in range(fine_tune_iterations):
+- radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+-
+- # Anneal subtle growth pressure (linear decay)
+- current_finetune_growth_pressure = finetune_growth_pressure_start - \
+- (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+- (fine_tune_iter / fine_tune_iterations)
+- pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+-
+- forces_ft = np.zeros((n, 2))
+-
+- # a. Vectorized Circle-to-circle repulsion forces
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- forces_ft += np.sum(force_matrix, axis=1)
+-
+- # b. Vectorized Wall repulsion forces
+- wall_forces_ft = np.zeros_like(centers_for_sim)
+- overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+- overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+- overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+- overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+-
+- wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+- wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+- wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+- wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+-
+- forces_ft += wall_forces_ft
+-
+- # Update center positions
+- current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+- centers_for_sim += forces_ft * current_fine_tune_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+- current_radii = compute_max_radii(centers_for_sim)
+- current_sum_radii = np.sum(current_radii)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers = centers_for_sim.copy() # Store the optimized centers
+- best_radii = current_radii.copy()
+-
+- # Fallback if no valid configuration was found (should not happen with good candidates)
+- if best_centers is None:
+- # This fallback uses the base 5x5 + center as a safe default
+- final_centers = np.zeros((n, 2))
+- final_centers[:25] = base_centers
+- final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+- final_radii = compute_max_radii(final_centers)
+- return final_centers, final_radii
+- else:
+- return best_centers, best_radii
+-
+-
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This function performs a full, highly convergent iteration.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence for final radii
+- for _ in range(500):
+- updated_in_pass = False
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- # If current radii would cause overlap and distance is not zero
+- if dist < 1e-9: # Handle cases where centers might be identical or very close
+- # If centers are practically the same, they cannot both have positive radius.
+- # For packing, it's safer to consider them as overlapping severely and shrink them.
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- # Scale both radii proportionally to just touch
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- # If a full pass over all pairs results in no change, radii have stabilized
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0) # Ensure no negative radii
+-
+-
+-def compute_radii_for_simulation(centers):
+- """
+- Compute approximate maximum radii for each circle position
+- for use inside the simulation loop. Fewer iterations for performance.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for more accurate radii during simulation, leveraging faster force computations.
+- for _ in range(400):
+- updated_in_pass = False
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9:
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0)
+-
++ packer_config = {
++ 'num_perturbation_trials': 12, # Increased trials for each candidate position
++ 'perturbation_scale': 0.02, # Increased perturbation for initial diversity
++
++ 'sim_iter': 3000, # Increased iterations for main refinement
++ 'radius_iter_sim': 150, # Reduced radii iterations for speed during sim
++ 'learning_rate': 0.018, # Slightly reduced learning rate for stability
++ 'wall_strength': 0.8, # Strong wall repulsion
++ 'initial_growth_pressure': 1.05, # Moderate initial growth pressure
++ 'final_growth_pressure': 1.0001, # Very subtle final growth pressure
++
++ 'fine_tune_iter': 1200, # Increased iterations for fine-tuning
++ 'radius_iter_ft': 300, # More accurate radii iterations for fine-tune
++ 'fine_tune_lr': 0.0008, # Very low learning rate for precision
++ 'fine_tune_growth_pressure_start': 1.0005, # Subtle growth pressure in fine-tune start
++ 'fine_tune_growth_pressure_end': 1.000001, # Even more subtle at fine-tune end
++
++ 'final_radius_iter': 2000 # High iterations for final radius computation
++ }
++
++ packer = HybridSimulatorPacker(n=n, config=packer_config)
++ centers, radii = packer.pack()
++
++ return centers, radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a1ee5679163e03ba9523dcbddc470ec604c9388
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/main.py
@@ -0,0 +1,242 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class HybridSimulatorPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy using force-directed simulation.
+ Hyperparameters are managed in a centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.best_centers_found = None
+ self.best_radii_found = None
+ self.max_sum_radii = -1.0
+
+ def _compute_radii_iterative(self, centers_to_eval, max_iter=750, convergence_threshold=1e-8):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling
+ with a configurable number of iterations and convergence check.
+ """
+ n_eval = centers_to_eval.shape[0]
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n_eval):
+ for j in range(i + 1, n_eval):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located or nearly co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Using a simplified convergence check for speed within simulation
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+ def _run_simulation_and_finetune(self, initial_centers):
+ """
+ Performs a two-phase force-directed simulation (main refinement + fine-tuning)
+ on a given initial configuration.
+ """
+ current_centers = initial_centers.copy()
+
+ # Phase 1: Main refinement with annealing growth pressure and learning rate
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2 # Quadratic annealing
+
+ current_radii = self._compute_radii_iterative(current_centers, max_iter=self.config['radius_iter_sim'])
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = current_centers[:, np.newaxis, :] - current_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces
+ wall_forces = np.zeros_like(current_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (current_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (current_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2 # Quadratic annealing for LR
+ current_centers += forces * lr
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with subtle growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ fine_tune_growth_pressure_start = self.config['fine_tune_growth_pressure_start']
+ fine_tune_growth_pressure_end = self.config['fine_tune_growth_pressure_end']
+
+ for ft_iter in range(iterations_ft):
+ progress_ft = ft_iter / iterations_ft
+ current_ft_growth_pressure = fine_tune_growth_pressure_end + \
+ (fine_tune_growth_pressure_start - fine_tune_growth_pressure_end) * \
+ (1.0 - progress_ft) # Linear annealing for fine-tune growth pressure
+
+ current_radii_ft = self._compute_radii_iterative(current_centers, max_iter=self.config['radius_iter_ft'])
+ pressured_radii_ft = current_radii_ft * current_ft_growth_pressure
+
+ diffs = current_centers[:, np.newaxis, :] - current_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+ overlaps_ft = np.maximum(0, radii_sums_ft - dists)
+ np.fill_diagonal(overlaps_ft, 0)
+
+ force_matrix_ft = diffs * (overlaps_ft / dists)[:, :, np.newaxis]
+ circle_forces_ft = np.sum(force_matrix_ft, axis=1)
+
+ wall_forces_ft = np.zeros_like(current_centers)
+ wall_forces_ft[:, 0] += wall_strength * np.maximum(0, pressured_radii_ft - current_centers[:, 0])
+ wall_forces_ft[:, 0] -= wall_strength * np.maximum(0, (current_centers[:, 0] + pressured_radii_ft) - 1.0)
+ wall_forces_ft[:, 1] += wall_strength * np.maximum(0, pressured_radii_ft - current_centers[:, 1])
+ wall_forces_ft[:, 1] -= wall_strength * np.maximum(0, (current_centers[:, 1] + pressured_radii_ft) - 1.0)
+
+ forces_ft = circle_forces_ft + wall_forces_ft
+ current_centers += forces_ft * lr_ft # Fixed LR for fine-tuning
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ # After both simulation phases, compute final accurate radii
+ final_radii = self._compute_radii_iterative(current_centers, max_iter=self.config['final_radius_iter'])
+ current_sum_radii = np.sum(final_radii)
+
+ if current_sum_radii > self.max_sum_radii:
+ self.max_sum_radii = current_sum_radii
+ self.best_centers_found = current_centers.copy()
+ self.best_radii_found = final_radii.copy()
+
+ def pack(self):
+ """
+ Executes the multi-start packing pipeline: initialize base grid,
+ iterate through candidate 26th circle positions, and run simulations.
+ """
+ # 1. Initialize 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers_25 = np.zeros((25, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers_25[k, 0] = (i + 0.5) * spacing
+ base_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # 2. Define strategic candidate placements for the 26th circle (expanded list)
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid (spacing=0.2)
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [spacing, 0.5], # (0.2, 0.5)
+ [0.5, spacing], # (0.5, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ # Corner-offset points (closer to edges/corners)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ # 3. Iterate through candidate positions and run multiple perturbed trials
+ for extra_pos_base in candidate_extra_positions:
+ for _ in range(self.config['num_perturbation_trials']):
+ current_centers = np.zeros((self.n, 2))
+
+ # Perturb the base 5x5 grid
+ perturbation_25 = np.random.normal(0, self.config['perturbation_scale'], size=base_centers_25.shape)
+ perturbed_base_centers = np.clip(base_centers_25 + perturbation_25, 0.0, 1.0)
+ current_centers[:25] = perturbed_base_centers
+
+ # Perturb the 26th circle's position
+ perturbation_extra = np.random.normal(0, self.config['perturbation_scale'], size=2)
+ current_centers[25] = np.clip(np.array(extra_pos_base) + perturbation_extra, 0.0, 1.0)
+
+ # Run the two-phase simulation for this initial configuration
+ self._run_simulation_and_finetune(current_centers)
+
+ # Fallback if no valid configuration was found (should not happen)
+ if self.best_centers_found is None:
+ # Default to a simple grid if no solution was better than initial -1.0
+ default_centers = np.zeros((self.n, 2))
+ default_centers[:25] = base_centers_25
+ default_centers[25] = [0.5, 0.5]
+ self.best_centers_found = default_centers
+ self.best_radii_found = self._compute_radii_iterative(default_centers, max_iter=self.config['final_radius_iter'])
+
+ return self.best_centers_found, self.best_radii_found
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Hybrid Simulator Packer.
+ """
+ n = 26
+ packer_config = {
+ 'num_perturbation_trials': 12, # Increased trials for each candidate position
+ 'perturbation_scale': 0.02, # Increased perturbation for initial diversity
+
+ 'sim_iter': 3000, # Increased iterations for main refinement
+ 'radius_iter_sim': 150, # Reduced radii iterations for speed during sim
+ 'learning_rate': 0.018, # Slightly reduced learning rate for stability
+ 'wall_strength': 0.8, # Strong wall repulsion
+ 'initial_growth_pressure': 1.05, # Moderate initial growth pressure
+ 'final_growth_pressure': 1.0001, # Very subtle final growth pressure
+
+ 'fine_tune_iter': 1200, # Increased iterations for fine-tuning
+ 'radius_iter_ft': 300, # More accurate radii iterations for fine-tune
+ 'fine_tune_lr': 0.0008, # Very low learning rate for precision
+ 'fine_tune_growth_pressure_start': 1.0005, # Subtle growth pressure in fine-tune start
+ 'fine_tune_growth_pressure_end': 1.000001, # Even more subtle at fine-tune end
+
+ 'final_radius_iter': 2000 # High iterations for final radius computation
+ }
+
+ packer = HybridSimulatorPacker(n=n, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..72549907846f0a8f8c2cee01685609035411074a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/original.py
@@ -0,0 +1,309 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Increased energy and stochasticity to "melt" the initial grid and escape local optima.
+ main_iterations = 2500
+ main_learning_rate = 0.02 # Higher LR for more initial movement
+ wall_repulsion_strength = 0.8 # Stronger walls to better use boundaries
+ initial_growth_pressure = 1.08 # Much higher initial pressure to break grid symmetry
+ final_growth_pressure = 1.0001
+
+ fine_tune_iterations = 1000
+ fine_tune_learning_rate = 0.001
+ finetune_growth_pressure_start = 1.0008
+ finetune_growth_pressure_end = 1.00001
+
+ # Stochastic multi-start parameters
+ num_perturbation_trials = 8 # Run multiple simulations for each candidate position
+ perturbation_scale = 0.01 # Scale of random noise to add to initial grid
+
+ # Iterate through candidate positions for the 26th circle and run multiple perturbed trials for each.
+ for extra_pos in candidate_extra_positions:
+ for _ in range(num_perturbation_trials):
+ current_centers = np.zeros((n, 2))
+
+ # Add random perturbation to the base 5x5 grid to break symmetry and "melt" the start
+ perturbation = np.random.normal(0, perturbation_scale, size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+
+ current_centers[:25] = perturbed_base_centers
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Main Physical Simulation Loop (Force-Directed Relaxation) ---
+ centers_for_sim = current_centers.copy() # Operate on a copy for simulation
+
+ for sim_iter in range(main_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal growth pressure quadratically
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / main_iterations))**2
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ forces = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii - dists)
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ # Avoid division by zero for identical centers, resulting in zero force
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = pressured_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + pressured_radii) - 1.0
+ overlap_bottom = pressured_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + pressured_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces += wall_forces # Add wall forces to total forces
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = main_learning_rate * (1.0 - (sim_iter / main_iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Post-Simulation Fine-Tuning Phase ---
+ # Continue from the best state of the main simulation for precision.
+ for fine_tune_iter in range(fine_tune_iterations):
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay)
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (fine_tune_iter / fine_tune_iterations)
+ pressured_radii_ft = radii_for_sim * current_finetune_growth_pressure
+
+ forces_ft = np.zeros((n, 2))
+
+ # a. Vectorized Circle-to-circle repulsion forces
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_pressured_radii_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_pressured_radii_ft - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ forces_ft += np.sum(force_matrix, axis=1)
+
+ # b. Vectorized Wall repulsion forces
+ wall_forces_ft = np.zeros_like(centers_for_sim)
+ overlap_left_ft = pressured_radii_ft - centers_for_sim[:, 0]
+ overlap_right_ft = (centers_for_sim[:, 0] + pressured_radii_ft) - 1.0
+ overlap_bottom_ft = pressured_radii_ft - centers_for_sim[:, 1]
+ overlap_top_ft = (centers_for_sim[:, 1] + pressured_radii_ft) - 1.0
+
+ wall_forces_ft[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left_ft)
+ wall_forces_ft[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right_ft)
+ wall_forces_ft[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom_ft)
+ wall_forces_ft[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top_ft)
+
+ forces_ft += wall_forces_ft
+
+ # Update center positions
+ current_fine_tune_lr = fine_tune_learning_rate * (1.0 - (fine_tune_iter / fine_tune_iterations)) # Linear annealing
+ centers_for_sim += forces_ft * current_fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # After both simulation phases, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for more accurate radii during simulation, leveraging faster force computations.
+ for _ in range(400):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..29370f2745803cc8e9345487ee2b09d004dbbdf3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results
+Run 1/1 completed in 1868.09 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9579505210508488
+ public: {'centers_str': ' centers[0] = (0.0900, 0.0397)\n centers[1] = (0.3315, 0.0715)\n centers[2] = (0.4789, 0.0968)\n centers[3] = (0.6858, 0.0831)\n centers[4] = (0.9086, 0.1008)\n centers[5] = (0.0466, 0.3534)\n centers[6] = (0.2969, 0.3671)\n centers[7] = (0.4979, 0.3067)\n centers[8] = (0.7054, 0.3218)\n centers[9] = (0.8957, 0.2850)\n centers[10] = (0.0923, 0.4946)\n centers[11] = (0.3131, 0.4943)\n centers[12] = (0.5300, 0.5109)\n centers[13] = (0.7300, 0.4731)\n centers[14] = (0.9086, 0.4645)\n centers[15] = (0.0809, 0.7016)\n centers[16] = (0.2492, 0.6613)\n centers[17] = (0.4834, 0.7107)\n centers[18] = (0.7248, 0.6422)\n centers[19] = (0.9186, 0.6731)\n centers[20] = (0.0877, 0.9113)\n centers[21] = (0.2928, 0.8808)\n centers[22] = (0.4859, 0.9235)\n centers[23] = (0.6834, 0.8770)\n centers[24] = (0.9028, 0.9025)\n centers[25] = (0.1861, 0.2131)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9579505210508488}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/packing_viz.png
+ execution_time_mean: 1868.0925680217333
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..ef33d1aed59f989eec191543451492eeb1d5f7a3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9579505210508488,
+ "public": {
+ "centers_str": " centers[0] = (0.0900, 0.0397)\n centers[1] = (0.3315, 0.0715)\n centers[2] = (0.4789, 0.0968)\n centers[3] = (0.6858, 0.0831)\n centers[4] = (0.9086, 0.1008)\n centers[5] = (0.0466, 0.3534)\n centers[6] = (0.2969, 0.3671)\n centers[7] = (0.4979, 0.3067)\n centers[8] = (0.7054, 0.3218)\n centers[9] = (0.8957, 0.2850)\n centers[10] = (0.0923, 0.4946)\n centers[11] = (0.3131, 0.4943)\n centers[12] = (0.5300, 0.5109)\n centers[13] = (0.7300, 0.4731)\n centers[14] = (0.9086, 0.4645)\n centers[15] = (0.0809, 0.7016)\n centers[16] = (0.2492, 0.6613)\n centers[17] = (0.4834, 0.7107)\n centers[18] = (0.7248, 0.6422)\n centers[19] = (0.9186, 0.6731)\n centers[20] = (0.0877, 0.9113)\n centers[21] = (0.2928, 0.8808)\n centers[22] = (0.4859, 0.9235)\n centers[23] = (0.6834, 0.8770)\n centers[24] = (0.9028, 0.9025)\n centers[25] = (0.1861, 0.2131)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9579505210508488
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/results/packing_viz.png",
+ "execution_time_mean": 1868.0925680217333,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fffff889a48bd9e901426d5366ef2f042e14fb7a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_87/rewrite.txt
@@ -0,0 +1,233 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class HybridSimulatorPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy using force-directed simulation.
+ Hyperparameters are managed in a centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.best_centers_found = None
+ self.best_radii_found = None
+ self.max_sum_radii = -1.0
+
+ def _compute_radii_iterative(self, centers_to_eval, max_iter=750, convergence_threshold=1e-8):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling
+ with a configurable number of iterations and convergence check.
+ """
+ n_eval = centers_to_eval.shape[0]
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n_eval):
+ for j in range(i + 1, n_eval):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located or nearly co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Using a simplified convergence check for speed within simulation
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+ def _run_simulation_and_finetune(self, initial_centers):
+ """
+ Performs a two-phase force-directed simulation (main refinement + fine-tuning)
+ on a given initial configuration.
+ """
+ current_centers = initial_centers.copy()
+
+ # Phase 1: Main refinement with annealing growth pressure and learning rate
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2 # Quadratic annealing
+
+ current_radii = self._compute_radii_iterative(current_centers, max_iter=self.config['radius_iter_sim'])
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = current_centers[:, np.newaxis, :] - current_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ # Wall forces
+ wall_forces = np.zeros_like(current_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (current_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - current_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (current_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2 # Quadratic annealing for LR
+ current_centers += forces * lr
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with subtle growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ fine_tune_growth_pressure_start = self.config['fine_tune_growth_pressure_start']
+ fine_tune_growth_pressure_end = self.config['fine_tune_growth_pressure_end']
+
+ for ft_iter in range(iterations_ft):
+ progress_ft = ft_iter / iterations_ft
+ current_ft_growth_pressure = fine_tune_growth_pressure_end + \
+ (fine_tune_growth_pressure_start - fine_tune_growth_pressure_end) * \
+ (1.0 - progress_ft) # Linear annealing for fine-tune growth pressure
+
+ current_radii_ft = self._compute_radii_iterative(current_centers, max_iter=self.config['radius_iter_ft'])
+ pressured_radii_ft = current_radii_ft * current_ft_growth_pressure
+
+ diffs = current_centers[:, np.newaxis, :] - current_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums_ft = pressured_radii_ft[:, np.newaxis] + pressured_radii_ft[np.newaxis, :]
+ overlaps_ft = np.maximum(0, radii_sums_ft - dists)
+ np.fill_diagonal(overlaps_ft, 0)
+
+ force_matrix_ft = diffs * (overlaps_ft / dists)[:, :, np.newaxis]
+ circle_forces_ft = np.sum(force_matrix_ft, axis=1)
+
+ wall_forces_ft = np.zeros_like(current_centers)
+ wall_forces_ft[:, 0] += wall_strength * np.maximum(0, pressured_radii_ft - current_centers[:, 0])
+ wall_forces_ft[:, 0] -= wall_strength * np.maximum(0, (current_centers[:, 0] + pressured_radii_ft) - 1.0)
+ wall_forces_ft[:, 1] += wall_strength * np.maximum(0, pressured_radii_ft - current_centers[:, 1])
+ wall_forces_ft[:, 1] -= wall_strength * np.maximum(0, (current_centers[:, 1] + pressured_radii_ft) - 1.0)
+
+ forces_ft = circle_forces_ft + wall_forces_ft
+ current_centers += forces_ft * lr_ft # Fixed LR for fine-tuning
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+
+ # After both simulation phases, compute final accurate radii
+ final_radii = self._compute_radii_iterative(current_centers, max_iter=self.config['final_radius_iter'])
+ current_sum_radii = np.sum(final_radii)
+
+ if current_sum_radii > self.max_sum_radii:
+ self.max_sum_radii = current_sum_radii
+ self.best_centers_found = current_centers.copy()
+ self.best_radii_found = final_radii.copy()
+
+ def pack(self):
+ """
+ Executes the multi-start packing pipeline: initialize base grid,
+ iterate through candidate 26th circle positions, and run simulations.
+ """
+ # 1. Initialize 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers_25 = np.zeros((25, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers_25[k, 0] = (i + 0.5) * spacing
+ base_centers_25[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # 2. Define strategic candidate placements for the 26th circle (expanded list)
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid (spacing=0.2)
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [spacing, 0.5], # (0.2, 0.5)
+ [0.5, spacing], # (0.5, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ # Corner-offset points (closer to edges/corners)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ # 3. Iterate through candidate positions and run multiple perturbed trials
+ for extra_pos_base in candidate_extra_positions:
+ for _ in range(self.config['num_perturbation_trials']):
+ current_centers = np.zeros((self.n, 2))
+
+ # Perturb the base 5x5 grid
+ perturbation_25 = np.random.normal(0, self.config['perturbation_scale'], size=base_centers_25.shape)
+ perturbed_base_centers = np.clip(base_centers_25 + perturbation_25, 0.0, 1.0)
+ current_centers[:25] = perturbed_base_centers
+
+ # Perturb the 26th circle's position
+ perturbation_extra = np.random.normal(0, self.config['perturbation_scale'], size=2)
+ current_centers[25] = np.clip(np.array(extra_pos_base) + perturbation_extra, 0.0, 1.0)
+
+ # Run the two-phase simulation for this initial configuration
+ self._run_simulation_and_finetune(current_centers)
+
+ # Fallback if no valid configuration was found (should not happen)
+ if self.best_centers_found is None:
+ # Default to a simple grid if no solution was better than initial -1.0
+ default_centers = np.zeros((self.n, 2))
+ default_centers[:25] = base_centers_25
+ default_centers[25] = [0.5, 0.5]
+ self.best_centers_found = default_centers
+ self.best_radii_found = self._compute_radii_iterative(default_centers, max_iter=self.config['final_radius_iter'])
+
+ return self.best_centers_found, self.best_radii_found
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Hybrid Simulator Packer.
+ """
+ n = 26
+ packer_config = {
+ 'num_perturbation_trials': 12, # Increased trials for each candidate position
+ 'perturbation_scale': 0.02, # Increased perturbation for initial diversity
+
+ 'sim_iter': 3000, # Increased iterations for main refinement
+ 'radius_iter_sim': 150, # Reduced radii iterations for speed during sim
+ 'learning_rate': 0.018, # Slightly reduced learning rate for stability
+ 'wall_strength': 0.8, # Strong wall repulsion
+ 'initial_growth_pressure': 1.05, # Moderate initial growth pressure
+ 'final_growth_pressure': 1.0001, # Very subtle final growth pressure
+
+ 'fine_tune_iter': 1200, # Increased iterations for fine-tuning
+ 'radius_iter_ft': 300, # More accurate radii iterations for fine-tune
+ 'fine_tune_lr': 0.0008, # Very low learning rate for precision
+ 'fine_tune_growth_pressure_start': 1.0005, # Subtle growth pressure in fine-tune start
+ 'fine_tune_growth_pressure_end': 1.000001, # Even more subtle at fine-tune end
+
+ 'final_radius_iter': 2000 # High iterations for final radius computation
+ }
+
+ packer = HybridSimulatorPacker(n=n, config=packer_config)
+ centers, radii = packer.pack()
+
+ return centers, radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..9e801bf220c36832db6e6ade6d2c88c029e2f332
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/edit.diff
@@ -0,0 +1,510 @@
+--- a/original.py
++++ b/original.py
+@@ -1,264 +1,267 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+-class ConfigurableHybridPacker:
+- """
+- A class-based packer encapsulating state and logic for a hybrid
+- 'initialize-and-refine' strategy. This object-oriented structure improves
+- maintainability and separates concerns, with hyperparameters managed in a
+- centralized configuration dictionary.
+- """
+- def __init__(self, initial_centers, config):
+- """Initializes the packer with the number of circles and a config dict."""
+- self.n = initial_centers.shape[0]
+- self.config = config
+- self.centers = np.array(initial_centers).copy() # Use provided initial centers
+- self.radii = np.zeros(self.n) # Will be filled later
+-
+- def _compute_radii_for_centers(self, centers_to_eval, iterations):
+- """
+- Computes maximum radii for a given set of centers using iterative proportional scaling.
+- This is a core utility used both during and after the simulation.
+- """
+- n_local = centers_to_eval.shape[0]
+- radii = np.min([
+- centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+- centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+- ], axis=0)
++# --- Core Utility Functions ---
++
++def compute_max_radii(centers, radius_iter=250):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ This uses an iterative proportional scaling method.
++ """
++ n = centers.shape[0]
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ for _ in range(radius_iter):
++ updated = False
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers[i] - centers[j]
++ dist = np.linalg.norm(vec)
++
++ if dist < 1e-9:
++ if radii[i] > 0 or radii[j] > 0:
++ radii[i], radii[j] = 0.0, 0.0
++ updated = True
++ continue
++
++ if radii[i] + radii[j] > dist:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++ if not updated:
++ break
++ return np.maximum(radii, 0.0)
++
++# --- Physical Simulation Refiner (Adapted from 'current' program's Packer) ---
++
++def physical_refinement(centers, config):
++ """
++ A functional version of the force-directed simulation to be used as a
++ local search operator within the Genetic Algorithm.
++ """
++ n = centers.shape[0]
++
++ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
++ for i_iter in range(config['sim_iter']):
++ progress = i_iter / config['sim_iter']
++ current_growth_pressure = config['final_growth_pressure'] + \
++ (config['initial_growth_pressure'] - config['final_growth_pressure']) * (1.0 - progress)**2
++
++ current_radii = compute_max_radii(centers, radius_iter=config['radius_calc_sim_iter'])
++ pressured_radii = current_radii * current_growth_pressure
++
++ forces = np.zeros_like(centers)
+
+- convergence_epsilon = 1e-7 # Define epsilon for dynamic convergence check
+-
+- for _ in range(iterations):
+- updated = False
+- previous_radii = radii.copy()
+-
+- for i in range(n_local):
+- for j in range(i + 1, n_local):
+- dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+-
+- if radii[i] + radii[j] > dist:
+- if dist < 1e-9: # Handle co-located centers
+- if radii[i] > 0 or radii[j] > 0:
+- radii[i], radii[j] = 0.0, 0.0
+- updated = True
+- else:
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+- updated = True
+-
+- max_delta_r = np.max(np.abs(radii - previous_radii))
+- if not updated and max_delta_r < convergence_epsilon: # More robust check for no updates
+- break
+- return np.maximum(radii, 0)
+-
+- def _run_refinement_simulation(self):
+- """
+- Performs a force-directed simulation to refine circle positions.
+- This simulation uses a 'growth pressure' concept to actively
+- encourage circles to expand and fill empty space.
+- """
+- iterations = self.config['sim_iter']
+- base_lr = self.config['learning_rate']
+- wall_strength = self.config['wall_strength']
+- initial_growth_pressure = self.config['initial_growth_pressure']
+- final_growth_pressure = self.config['final_growth_pressure']
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = radii_sums - dists
++ overlaps[overlaps < 0] = 0.0
++ np.fill_diagonal(overlaps, 0)
++
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ forces[:, 0] += config['wall_strength'] * np.maximum(0, pressured_radii - centers[:, 0])
++ forces[:, 0] -= config['wall_strength'] * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
++ forces[:, 1] += config['wall_strength'] * np.maximum(0, pressured_radii - centers[:, 1])
++ forces[:, 1] -= config['wall_strength'] * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
++
++ lr = config['learning_rate'] * (1.0 - progress)**2
++ centers += forces * lr
++ centers = np.clip(centers, 0.0, 1.0)
++
++ # --- Phase 2: Fine-Tuning (No Growth Pressure) ---
++ for _ in range(config['fine_tune_iter']):
++ current_radii = compute_max_radii(centers, radius_iter=config['radius_calc_sim_iter'])
++ forces = np.zeros_like(centers)
++
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
++ overlaps = radii_sums - dists
++ overlaps[overlaps < 0] = 0.0
++ np.fill_diagonal(overlaps, 0)
+
+- # Use radius_calc_sim_iter during simulation phases
+- radius_iterations_sim = self.config['radius_calc_sim_iter']
+-
+- for i_iter in range(iterations):
+- progress = i_iter / iterations
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+-
+- current_radii = self._compute_radii_for_centers(self.centers, radius_iterations_sim) # Pass iterations
+- pressured_radii = current_radii * current_growth_pressure
+-
+- forces = np.zeros_like(self.centers)
+-
+- diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9
+-
+- radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0
+- np.fill_diagonal(overlaps, 0)
+-
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0])
+- forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0)
+- forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1])
+- forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0)
+-
+- lr = base_lr * (1.0 - (i_iter / iterations))**2
+- self.centers += forces * lr
+- self.centers = np.clip(self.centers, 0.0, 1.0)
+-
+- def _run_fine_tuning(self):
+- """
+- Performs a final, short simulation with no growth pressure. This allows
+- the packing to settle and resolve any minor residual overlaps from the
+- main simulation, enabling a more precise final state.
+- """
+- iterations = self.config['fine_tune_iter']
+- lr = self.config['fine_tune_lr']
+- wall_strength = self.config['wall_strength']
+-
+- # Use radius_calc_sim_iter during simulation phases
+- radius_iterations_sim = self.config['radius_calc_sim_iter']
+-
+- for _ in range(iterations):
+- current_radii = self._compute_radii_for_centers(self.centers, radius_iterations_sim) # Pass iterations
+-
+- forces = np.zeros_like(self.centers)
+-
+- diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9
+-
+- radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0
+- np.fill_diagonal(overlaps, 0)
+-
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+- forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+- forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+- forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+-
+- self.centers += forces * lr
+- self.centers = np.clip(self.centers, 0.0, 1.0)
+-
+- def pack(self):
+- """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+- # Initial centers are already set in __init__
+- self._run_refinement_simulation()
+- self._run_fine_tuning()
+- # Final computation of radii for the refined centers.
+- self.radii = self._compute_radii_for_centers(self.centers, self.config['radius_calc_final_iter']) # Pass iterations
+- return self.centers, self.radii
+-
+-def construct_packing():
+- """
+- Main function to construct the circle packing. This implementation uses a multi-start
+- strategy with a ConfigurableHybridPacker to refine promising initial configurations.
+- """
+- n = 26
+-
+- packer_config = {
+- 'sim_iter': 2500, # Increased iterations for more thorough refinement.
+- 'radius_calc_sim_iter': 150, # Iterations for radii during simulation (lower for speed).
+- 'radius_calc_final_iter': 500, # Iterations for final radii calculation (higher for precision).
+- 'learning_rate': 0.02, # A balanced learning rate for exploration.
+- 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+- 'initial_growth_pressure': 1.03, # Stronger initial pressure to 'unfreeze' the grid.
+- 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+- 'fine_tune_iter': 600, # Increased iterations for final settlement.
+- 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+- }
+-
+- # Initial 5x5 grid setup for 25 circles
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ forces[:, 0] += config['wall_strength'] * np.maximum(0, current_radii - centers[:, 0])
++ forces[:, 0] -= config['wall_strength'] * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
++ forces[:, 1] += config['wall_strength'] * np.maximum(0, current_radii - centers[:, 1])
++ forces[:, 1] -= config['wall_strength'] * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
++
++ centers += forces * config['fine_tune_lr']
++ centers = np.clip(centers, 0.0, 1.0)
++
++ return centers
++
++# --- Memetic Algorithm Components ---
++
++class Individual:
++ def __init__(self, centers_array, n_circles):
++ self.centers = np.array(centers_array).reshape(n_circles, 2)
++ self.radii = None
++ self.fitness = -1.0
++
++ def evaluate_fitness(self, radius_iter_val):
++ self.radii = compute_max_radii(self.centers, radius_iter=radius_iter_val)
++ self.fitness = np.sum(self.radii)
++ return self.fitness
++
++def initialize_population(n_circles, population_size, refinement_config):
++ population = []
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+- base_grid_centers = np.zeros((n - 1, 2)) # 25 circles
++
++ # Base grid for seeding
++ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+- base_grid_centers[k, 0] = (i + 0.5) * spacing
+- base_grid_centers[k, 1] = (j + 0.5) * spacing
++ grid_centers[k, 0] = (i + 0.5) * spacing
++ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+- # Strategic candidate placements for the 26th circle.
+- # A mix of interstitial, corner-offset, and mid-edge points.
+- candidate_extra_positions_base = [
+- [spacing, spacing], # (0.2, 0.2)
+- [spacing, 0.5], # (0.2, 0.5)
+- [spacing, 1 - spacing], # (0.2, 0.8)
+- [0.5, spacing], # (0.5, 0.2)
+- [0.5, 0.5], # (0.5, 0.5) - Center
+- [0.5, 1 - spacing], # (0.5, 0.8)
+- [1 - spacing, spacing], # (0.8, 0.2)
+- [1 - spacing, 0.5], # (0.8, 0.5)
+- [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+- [0.05, 0.5], [0.5, 0.05], [0.95, 0.5], [0.5, 0.95], # Edge midpoints
+- # Add a few more slightly varied points near previous strategic ones
+- [spacing * 1.2, spacing * 1.2], # Slightly offset from (0.2, 0.2)
+- [0.5 - spacing/4, 0.5 + spacing/4], # Slightly off-center
+- [1 - spacing * 1.2, 1 - spacing * 1.2] # Slightly offset from (0.8, 0.8)
++ # 1. Add refined starting individuals for strong seeding.
++ candidate_extra_positions = [
++ [spacing, spacing], # (0.2, 0.2)
++ [spacing, 0.5], # (0.2, 0.5)
++ [0.5, 0.5], # (0.5, 0.5)
++ [0.05, 0.05], # Corner
++ [0.95, 0.95], # Corner
+ ]
+-
+- # Introduce some randomness to the starting positions for greater diversity
+- # For each base position, generate a few perturbed versions
+- expanded_candidate_positions = []
+- num_perturbations_per_base = 2 # Generate N perturbed versions for each base position
+- perturbation_strength_initial_extra = 0.02 # For the 26th circle
+-
+- for base_pos in candidate_extra_positions_base:
+- expanded_candidate_positions.append(np.array(base_pos)) # Include the exact base position
+- for _ in range(num_perturbations_per_base - 1): # Generate (N-1) perturbed versions
+- perturbed_pos = np.array(base_pos) + np.random.normal(0, perturbation_strength_initial_extra, 2)
+- expanded_candidate_positions.append(np.clip(perturbed_pos, 0.0, 1.0))
+-
+- best_sum_radii = -1.0
+- best_centers = None
+- best_radii = None
+-
+- # Iterate through expanded candidate positions for the 26th circle
+- for extra_pos_candidate in expanded_candidate_positions:
+- current_centers = np.zeros((n, 2))
+- current_centers[:n-1] = base_grid_centers.copy() # Use copy to ensure independence
++ for extra_pos in candidate_extra_positions:
++ if len(population) >= population_size: break
++ initial_centers = grid_centers.copy()
++ initial_centers[n_circles - 1] = extra_pos
++ refined_centers = physical_refinement(initial_centers, refinement_config)
++ population.append(Individual(refined_centers, n_circles))
++
++ # 2. Add perturbed versions of the base grid
++ base_grid_with_extra = grid_centers.copy()
++ base_grid_with_extra[n_circles - 1] = [spacing, spacing]
++ for _ in range(population_size // 5):
++ if len(population) >= population_size: break
++ perturbed_centers = base_grid_with_extra + np.random.normal(0, 0.02, (n_circles, 2))
++ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
++
++ # 3. Fill the rest with random configurations for diversity
++ while len(population) < population_size:
++ random_centers = np.random.rand(n_circles, 2)
++ population.append(Individual(random_centers, n_circles))
++
++ return population
++
++def select_parents(population, tournament_size):
++ tournament_candidates_1 = np.random.choice(population, tournament_size, replace=False)
++ parent1 = max(tournament_candidates_1, key=lambda ind: ind.fitness)
++
++ tournament_candidates_2 = np.random.choice(population, tournament_size, replace=False)
++ parent2 = max(tournament_candidates_2, key=lambda ind: ind.fitness)
++
++ return parent1, parent2
++
++def crossover(parent1, parent2, n_circles):
++ child_centers = np.zeros((n_circles, 2))
++ # Uniform crossover
++ mask = np.random.rand(n_circles) < 0.5
++ child_centers[mask] = parent1.centers[mask]
++ child_centers[~mask] = parent2.centers[~mask]
++ return Individual(child_centers, n_circles)
++
++def mutate_and_refine(individual, mutation_rate, mutation_strength, refinement_prob, refinement_config, n_circles):
++ mutated_centers = individual.centers.copy()
++
++ # 1. Standard Gaussian Mutation
++ for i in range(n_circles):
++ if np.random.rand() < mutation_rate:
++ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
++
++ # 2. Memetic Step: Local refinement via physical simulation
++ if np.random.rand() < refinement_prob:
++ mutated_centers = physical_refinement(mutated_centers, refinement_config)
++
++ return Individual(np.clip(mutated_centers, 0.0, 1.0), n_circles)
++
++def construct_packing():
++ n_circles = 26
++
++ # --- Memetic Algorithm Hyperparameters ---
++ POPULATION_SIZE = 120
++ NUM_GENERATIONS = 1200
++ MUTATION_RATE = 0.25
++ INITIAL_MUTATION_STRENGTH = 0.06
++ FINAL_MUTATION_STRENGTH = 0.001
++ TOURNAMENT_SIZE = 6
++ ELITISM_COUNT = 8
++
++ # --- Local Search (Refinement) Hyperparameters ---
++ REFINEMENT_PROBABILITY = 0.18
++ refinement_config = {
++ 'sim_iter': 150,
++ 'fine_tune_iter': 80,
++ 'radius_calc_sim_iter': 60,
++ 'learning_rate': 0.02,
++ 'wall_strength': 0.9,
++ 'initial_growth_pressure': 1.04,
++ 'final_growth_pressure': 1.001,
++ 'fine_tune_lr': 0.001,
++ }
++
++ GA_RADIUS_ITER = 80
++ FINAL_RADIUS_ITER = 500
++
++ population = initialize_population(n_circles, POPULATION_SIZE, refinement_config)
++
++ for individual in population:
++ individual.evaluate_fitness(GA_RADIUS_ITER)
++
++ best_individual_overall = max(population, key=lambda ind: ind.fitness)
++
++ for generation in range(NUM_GENERATIONS):
++ progress = generation / NUM_GENERATIONS
++ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
++ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
++
++ new_population = []
++ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
++ new_population.extend(sorted_population[:ELITISM_COUNT])
++
++ while len(new_population) < POPULATION_SIZE:
++ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
++ child = crossover(parent1, parent2, n_circles)
++ child = mutate_and_refine(child, MUTATION_RATE, current_mutation_strength, REFINEMENT_PROBABILITY, refinement_config, n_circles)
++ child.evaluate_fitness(GA_RADIUS_ITER)
++ new_population.append(child)
+
+- # Apply a small random perturbation to the base grid centers
+- perturbation_strength_grid = 0.005 * spacing # E.g., 5% of grid spacing
+- current_centers[:n-1] += np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+-
+- # Set the 26th circle's initial position
+- current_centers[n-1] = np.array(extra_pos_candidate)
+-
+- # Ensure all initial centers are within bounds
+- initial_centers_clipped = np.clip(current_centers, 0.0, 1.0)
+-
+- # Create a new packer instance for each initial configuration
+- packer = ConfigurableHybridPacker(initial_centers_clipped, packer_config)
+-
+- # Run the packing process for this configuration
+- centers_after_packing, radii_after_packing = packer.pack()
+-
+- current_sum_radii = np.sum(radii_after_packing)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers = centers_after_packing.copy()
+- best_radii = radii_after_packing.copy()
+-
+- # Fallback if no valid configuration was found (should not happen with good candidates)
+- if best_centers is None:
+- # Default to a simple 5x5 grid plus the first extra position candidate
+- default_centers = np.zeros((n, 2))
+- default_centers[:n-1] = base_grid_centers
+- default_centers[n-1] = expanded_candidate_positions[0]
+- default_centers_clipped = np.clip(default_centers, 0.0, 1.0)
+- default_packer = ConfigurableHybridPacker(default_centers_clipped, packer_config)
+- best_centers, best_radii = default_packer.pack() # Re-run for a guaranteed result
+-
+- return best_centers, best_radii
++ population = new_population
++
++ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
++ if current_best_in_gen.fitness > best_individual_overall.fitness:
++ best_individual_overall = current_best_in_gen
++
++ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
++
++ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c8fa917a09189a3d8daebe1c741bba1ee5097ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/main.py
@@ -0,0 +1,267 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility Functions ---
+
+def compute_max_radii(centers, radius_iter=250):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(radius_iter):
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0.0)
+
+# --- Physical Simulation Refiner (Adapted from 'current' program's Packer) ---
+
+def physical_refinement(centers, config):
+ """
+ A functional version of the force-directed simulation to be used as a
+ local search operator within the Genetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(config['sim_iter']):
+ progress = i_iter / config['sim_iter']
+ current_growth_pressure = config['final_growth_pressure'] + \
+ (config['initial_growth_pressure'] - config['final_growth_pressure']) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, radius_iter=config['radius_calc_sim_iter'])
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += config['wall_strength'] * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= config['wall_strength'] * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += config['wall_strength'] * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= config['wall_strength'] * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = config['learning_rate'] * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning (No Growth Pressure) ---
+ for _ in range(config['fine_tune_iter']):
+ current_radii = compute_max_radii(centers, radius_iter=config['radius_calc_sim_iter'])
+ forces = np.zeros_like(centers)
+
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += config['wall_strength'] * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= config['wall_strength'] * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += config['wall_strength'] * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= config['wall_strength'] * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * config['fine_tune_lr']
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+# --- Memetic Algorithm Components ---
+
+class Individual:
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, radius_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size, refinement_config):
+ population = []
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ # Base grid for seeding
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # 1. Add refined starting individuals for strong seeding.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [0.5, 0.5], # (0.5, 0.5)
+ [0.05, 0.05], # Corner
+ [0.95, 0.95], # Corner
+ ]
+ for extra_pos in candidate_extra_positions:
+ if len(population) >= population_size: break
+ initial_centers = grid_centers.copy()
+ initial_centers[n_circles - 1] = extra_pos
+ refined_centers = physical_refinement(initial_centers, refinement_config)
+ population.append(Individual(refined_centers, n_circles))
+
+ # 2. Add perturbed versions of the base grid
+ base_grid_with_extra = grid_centers.copy()
+ base_grid_with_extra[n_circles - 1] = [spacing, spacing]
+ for _ in range(population_size // 5):
+ if len(population) >= population_size: break
+ perturbed_centers = base_grid_with_extra + np.random.normal(0, 0.02, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill the rest with random configurations for diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ tournament_candidates_1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_candidates_1, key=lambda ind: ind.fitness)
+
+ tournament_candidates_2 = np.random.choice(population, tournament_size, replace=False)
+ parent2 = max(tournament_candidates_2, key=lambda ind: ind.fitness)
+
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ child_centers = np.zeros((n_circles, 2))
+ # Uniform crossover
+ mask = np.random.rand(n_circles) < 0.5
+ child_centers[mask] = parent1.centers[mask]
+ child_centers[~mask] = parent2.centers[~mask]
+ return Individual(child_centers, n_circles)
+
+def mutate_and_refine(individual, mutation_rate, mutation_strength, refinement_prob, refinement_config, n_circles):
+ mutated_centers = individual.centers.copy()
+
+ # 1. Standard Gaussian Mutation
+ for i in range(n_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+
+ # 2. Memetic Step: Local refinement via physical simulation
+ if np.random.rand() < refinement_prob:
+ mutated_centers = physical_refinement(mutated_centers, refinement_config)
+
+ return Individual(np.clip(mutated_centers, 0.0, 1.0), n_circles)
+
+def construct_packing():
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 120
+ NUM_GENERATIONS = 1200
+ MUTATION_RATE = 0.25
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 6
+ ELITISM_COUNT = 8
+
+ # --- Local Search (Refinement) Hyperparameters ---
+ REFINEMENT_PROBABILITY = 0.18
+ refinement_config = {
+ 'sim_iter': 150,
+ 'fine_tune_iter': 80,
+ 'radius_calc_sim_iter': 60,
+ 'learning_rate': 0.02,
+ 'wall_strength': 0.9,
+ 'initial_growth_pressure': 1.04,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_lr': 0.001,
+ }
+
+ GA_RADIUS_ITER = 80
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE, refinement_config)
+
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate_and_refine(child, MUTATION_RATE, current_mutation_strength, REFINEMENT_PROBABILITY, refinement_config, n_circles)
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..55e367338d20dbca3936b5be63a836a297e242f2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/original.py
@@ -0,0 +1,264 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, initial_centers, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = initial_centers.shape[0]
+ self.config = config
+ self.centers = np.array(initial_centers).copy() # Use provided initial centers
+ self.radii = np.zeros(self.n) # Will be filled later
+
+ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ n_local = centers_to_eval.shape[0]
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-7 # Define epsilon for dynamic convergence check
+
+ for _ in range(iterations):
+ updated = False
+ previous_radii = radii.copy()
+
+ for i in range(n_local):
+ for j in range(i + 1, n_local):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated and max_delta_r < convergence_epsilon: # More robust check for no updates
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ # Use radius_calc_sim_iter during simulation phases
+ radius_iterations_sim = self.config['radius_calc_sim_iter']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_centers(self.centers, radius_iterations_sim) # Pass iterations
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr']
+ wall_strength = self.config['wall_strength']
+
+ # Use radius_calc_sim_iter during simulation phases
+ radius_iterations_sim = self.config['radius_calc_sim_iter']
+
+ for _ in range(iterations):
+ current_radii = self._compute_radii_for_centers(self.centers, radius_iterations_sim) # Pass iterations
+
+ forces = np.zeros_like(self.centers)
+
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+
+ self.centers += forces * lr
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ # Initial centers are already set in __init__
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers.
+ self.radii = self._compute_radii_for_centers(self.centers, self.config['radius_calc_final_iter']) # Pass iterations
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation uses a multi-start
+ strategy with a ConfigurableHybridPacker to refine promising initial configurations.
+ """
+ n = 26
+
+ packer_config = {
+ 'sim_iter': 2500, # Increased iterations for more thorough refinement.
+ 'radius_calc_sim_iter': 150, # Iterations for radii during simulation (lower for speed).
+ 'radius_calc_final_iter': 500, # Iterations for final radii calculation (higher for precision).
+ 'learning_rate': 0.02, # A balanced learning rate for exploration.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.03, # Stronger initial pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.001, # End with gentle pressure to tighten the packing.
+ 'fine_tune_iter': 600, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_grid_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_grid_centers[k, 0] = (i + 0.5) * spacing
+ base_grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # A mix of interstitial, corner-offset, and mid-edge points.
+ candidate_extra_positions_base = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.05, 0.5], [0.5, 0.05], [0.95, 0.5], [0.5, 0.95], # Edge midpoints
+ # Add a few more slightly varied points near previous strategic ones
+ [spacing * 1.2, spacing * 1.2], # Slightly offset from (0.2, 0.2)
+ [0.5 - spacing/4, 0.5 + spacing/4], # Slightly off-center
+ [1 - spacing * 1.2, 1 - spacing * 1.2] # Slightly offset from (0.8, 0.8)
+ ]
+
+ # Introduce some randomness to the starting positions for greater diversity
+ # For each base position, generate a few perturbed versions
+ expanded_candidate_positions = []
+ num_perturbations_per_base = 2 # Generate N perturbed versions for each base position
+ perturbation_strength_initial_extra = 0.02 # For the 26th circle
+
+ for base_pos in candidate_extra_positions_base:
+ expanded_candidate_positions.append(np.array(base_pos)) # Include the exact base position
+ for _ in range(num_perturbations_per_base - 1): # Generate (N-1) perturbed versions
+ perturbed_pos = np.array(base_pos) + np.random.normal(0, perturbation_strength_initial_extra, 2)
+ expanded_candidate_positions.append(np.clip(perturbed_pos, 0.0, 1.0))
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through expanded candidate positions for the 26th circle
+ for extra_pos_candidate in expanded_candidate_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:n-1] = base_grid_centers.copy() # Use copy to ensure independence
+
+ # Apply a small random perturbation to the base grid centers
+ perturbation_strength_grid = 0.005 * spacing # E.g., 5% of grid spacing
+ current_centers[:n-1] += np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+
+ # Set the 26th circle's initial position
+ current_centers[n-1] = np.array(extra_pos_candidate)
+
+ # Ensure all initial centers are within bounds
+ initial_centers_clipped = np.clip(current_centers, 0.0, 1.0)
+
+ # Create a new packer instance for each initial configuration
+ packer = ConfigurableHybridPacker(initial_centers_clipped, packer_config)
+
+ # Run the packing process for this configuration
+ centers_after_packing, radii_after_packing = packer.pack()
+
+ current_sum_radii = np.sum(radii_after_packing)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_after_packing.copy()
+ best_radii = radii_after_packing.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # Default to a simple 5x5 grid plus the first extra position candidate
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_grid_centers
+ default_centers[n-1] = expanded_candidate_positions[0]
+ default_centers_clipped = np.clip(default_centers, 0.0, 1.0)
+ default_packer = ConfigurableHybridPacker(default_centers_clipped, packer_config)
+ best_centers, best_radii = default_packer.pack() # Re-run for a guaranteed result
+
+ return best_centers, best_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..844af7f710154642fd48ed7c8226df821fee2ced
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_88/rewrite.txt
@@ -0,0 +1,258 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility Functions ---
+
+def compute_max_radii(centers, radius_iter=250):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(radius_iter):
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0.0)
+
+# --- Physical Simulation Refiner (Adapted from 'current' program's Packer) ---
+
+def physical_refinement(centers, config):
+ """
+ A functional version of the force-directed simulation to be used as a
+ local search operator within the Genetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(config['sim_iter']):
+ progress = i_iter / config['sim_iter']
+ current_growth_pressure = config['final_growth_pressure'] + \
+ (config['initial_growth_pressure'] - config['final_growth_pressure']) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, radius_iter=config['radius_calc_sim_iter'])
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += config['wall_strength'] * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= config['wall_strength'] * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += config['wall_strength'] * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= config['wall_strength'] * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = config['learning_rate'] * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning (No Growth Pressure) ---
+ for _ in range(config['fine_tune_iter']):
+ current_radii = compute_max_radii(centers, radius_iter=config['radius_calc_sim_iter'])
+ forces = np.zeros_like(centers)
+
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += config['wall_strength'] * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= config['wall_strength'] * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += config['wall_strength'] * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= config['wall_strength'] * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * config['fine_tune_lr']
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+# --- Memetic Algorithm Components ---
+
+class Individual:
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, radius_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size, refinement_config):
+ population = []
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+
+ # Base grid for seeding
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # 1. Add refined starting individuals for strong seeding.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [0.5, 0.5], # (0.5, 0.5)
+ [0.05, 0.05], # Corner
+ [0.95, 0.95], # Corner
+ ]
+ for extra_pos in candidate_extra_positions:
+ if len(population) >= population_size: break
+ initial_centers = grid_centers.copy()
+ initial_centers[n_circles - 1] = extra_pos
+ refined_centers = physical_refinement(initial_centers, refinement_config)
+ population.append(Individual(refined_centers, n_circles))
+
+ # 2. Add perturbed versions of the base grid
+ base_grid_with_extra = grid_centers.copy()
+ base_grid_with_extra[n_circles - 1] = [spacing, spacing]
+ for _ in range(population_size // 5):
+ if len(population) >= population_size: break
+ perturbed_centers = base_grid_with_extra + np.random.normal(0, 0.02, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill the rest with random configurations for diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ tournament_candidates_1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_candidates_1, key=lambda ind: ind.fitness)
+
+ tournament_candidates_2 = np.random.choice(population, tournament_size, replace=False)
+ parent2 = max(tournament_candidates_2, key=lambda ind: ind.fitness)
+
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ child_centers = np.zeros((n_circles, 2))
+ # Uniform crossover
+ mask = np.random.rand(n_circles) < 0.5
+ child_centers[mask] = parent1.centers[mask]
+ child_centers[~mask] = parent2.centers[~mask]
+ return Individual(child_centers, n_circles)
+
+def mutate_and_refine(individual, mutation_rate, mutation_strength, refinement_prob, refinement_config, n_circles):
+ mutated_centers = individual.centers.copy()
+
+ # 1. Standard Gaussian Mutation
+ for i in range(n_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+
+ # 2. Memetic Step: Local refinement via physical simulation
+ if np.random.rand() < refinement_prob:
+ mutated_centers = physical_refinement(mutated_centers, refinement_config)
+
+ return Individual(np.clip(mutated_centers, 0.0, 1.0), n_circles)
+
+def construct_packing():
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 120
+ NUM_GENERATIONS = 1200
+ MUTATION_RATE = 0.25
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 6
+ ELITISM_COUNT = 8
+
+ # --- Local Search (Refinement) Hyperparameters ---
+ REFINEMENT_PROBABILITY = 0.18
+ refinement_config = {
+ 'sim_iter': 150,
+ 'fine_tune_iter': 80,
+ 'radius_calc_sim_iter': 60,
+ 'learning_rate': 0.02,
+ 'wall_strength': 0.9,
+ 'initial_growth_pressure': 1.04,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_lr': 0.001,
+ }
+
+ GA_RADIUS_ITER = 80
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE, refinement_config)
+
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate_and_refine(child, MUTATION_RATE, current_mutation_strength, REFINEMENT_PROBABILITY, refinement_config, n_circles)
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e5647bdcd68e2907de2f3508f9356bc8354fcb5d
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..e0a9165a4878ed90430adac11d3d9a7b473cd0c9
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/edit.diff
@@ -0,0 +1,502 @@
+--- a/original.py
++++ b/original.py
+@@ -1,307 +1,248 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
+-
+ import numpy as np
+
+-
+-def construct_packing():
+- """
+- Constructs a specific arrangement of 26 circles in a unit square
+- by trying multiple initial placements for the 26th circle
+- and refining each using a two-phase force-directed simulation.
+- The best resulting packing (highest sum of radii) is then returned.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- n = 26
+-
+- # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+- sim_iter = 2500 # Increased iterations for more thorough refinement.
+- learning_rate = 0.02 # A balanced learning rate for exploration.
+- wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+- initial_growth_pressure = 1.04 # Increased pressure to better 'unfreeze' the grid.
+- final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+- fine_tune_iter = 600 # Increased iterations for final settlement.
+- fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+- radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+- final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+- num_trials_per_candidate = 3 # Number of simulations per candidate starting position.
+-
+- # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- base_25_centers = np.zeros((n - 1, 2))
+- k = 0
+- for j in range(num_cells_side):
+- for i in range(num_cells_side):
+- base_25_centers[k, 0] = (i + 0.5) * spacing
+- base_25_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- # Strategic candidate placements for the 26th circle.
+- # Expanded list to explore a wider range of starting configurations.
+- candidate_extra_positions = [
+- # Interstitial points from 5x5 grid
+- [spacing, spacing], # (0.2, 0.2)
+- [spacing, 0.5], # (0.2, 0.5)
+- [spacing, 1 - spacing], # (0.2, 0.8)
+- [0.5, spacing], # (0.5, 0.2)
+- [0.5, 0.5], # (0.5, 0.5) - Center
+- [0.5, 1 - spacing], # (0.5, 0.8)
+- [1 - spacing, spacing], # (0.8, 0.2)
+- [1 - spacing, 0.5], # (0.8, 0.5)
+- [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- # Corner-offset points
+- [0.05, 0.05],
+- [0.05, 0.95],
+- [0.95, 0.05],
+- [0.95, 0.95],
+- # Edge midpoints
+- [0.05, 0.5],
+- [0.5, 0.05],
+- [0.95, 0.5],
+- [0.5, 0.95]
+- ]
+-
+- best_sum_radii = -1.0
+- best_centers_overall = None
+- best_radii_overall = None
+-
+- # Add a stronger perturbation to the base grid to better break symmetry and encourage rearrangement.
+- perturbation_strength = 0.025 * spacing # 2.5% of the grid spacing
+-
+- # Iterate through each candidate position, running multiple trials for each to find a better optimum.
+- for extra_pos in candidate_extra_positions:
+- for _ in range(num_trials_per_candidate):
+- # Create a new 'centers' array for this trial
+- current_centers_trial = np.zeros((n, 2))
+-
+- # Start with a fresh copy of the base grid and perturb it
+- perturbed_grid = base_25_centers.copy()
+- perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+- current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+-
+- current_centers_trial[n-1] = np.array(extra_pos)
+-
+- # Refine these centers using the simulation, passing all necessary hyperparameters
+- refined_centers = refine_centers_with_simulation(
+- current_centers_trial,
+- sim_iter, learning_rate, wall_strength,
+- initial_growth_pressure, final_growth_pressure,
+- fine_tune_iter, fine_tune_lr, radius_sim_iter
+- )
+-
+- # Compute the final, precise radii for this refined configuration
+- current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+- current_sum_radii = np.sum(current_radii_final)
+-
+- # Update if this is the best packing found so far
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers_overall = refined_centers.copy()
+- best_radii_overall = current_radii_final.copy()
+-
+- # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+- if best_centers_overall is None:
+- # Revert to a safe default if no better solution was found
+- default_centers = np.zeros((n, 2))
+- default_centers[:n-1] = base_25_centers
+- default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+- default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+- return default_centers, default_radii
+- else:
+- return best_centers_overall, best_radii_overall
+-
+-
+-def refine_centers_with_simulation(
+- centers,
+- sim_iter, learning_rate, wall_strength,
+- initial_growth_pressure, final_growth_pressure,
+- fine_tune_iter, fine_tune_lr, radius_sim_iter
+-):
+- """
+- Refines circle centers using a two-phase force-directed simulation.
+- It first uses an annealed 'growth pressure' to explore the solution space,
+- then fine-tunes the positions with actual overlaps.
+-
+- Args:
+- centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+- sim_iter: Number of iterations for the main simulation phase.
+- learning_rate: Base learning rate for center updates.
+- wall_strength: Strength of repulsion from square walls.
+- initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+- final_growth_pressure: Final multiplier for radii.
+- fine_tune_iter: Number of iterations for the fine-tuning phase.
+- fine_tune_lr: Learning rate for the fine-tuning phase.
+- radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+-
+- Returns:
+- np.array: Refined circle centers.
++def compute_max_radii(centers, iterations=500):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+-
+- # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
++ # Initialize radii based on distance to walls, which is the hard upper limit.
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
++
++ for _ in range(iterations):
++ updated_in_pass = False
++ previous_radii = radii.copy()
++
++ # Iteratively shrink radii that cause overlaps
++ for i in range(n):
++ for j in range(i + 1, n):
++ dist = np.linalg.norm(centers[i] - centers[j])
++
++ if dist < 1e-9: # Handle co-located centers
++ if radii[i] > 0 or radii[j] > 0:
++ radii[i], radii[j] = 0.0, 0.0
++ updated_in_pass = True
++ continue
++
++ if radii[i] + radii[j] > dist:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated_in_pass = True
++
++ # Check for convergence
++ max_delta_r = np.max(np.abs(radii - previous_radii))
++ if not updated_in_pass and max_delta_r < convergence_epsilon:
++ break
++
++ return np.maximum(radii, 0.0)
++
++def hybrid_local_searcher(centers, sim_iter=120, fine_tune_iter=30, radius_sim_iter=35):
++ """
++ Refines circle centers using a two-phase force-directed simulation. This acts as
++ the local search operator for the Memetic Algorithm.
++ """
++ n = centers.shape[0]
++
++ # Tuned hyperparameters for fast but effective local optimization
++ learning_rate = 0.02
++ wall_strength = 0.8
++ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
++ final_growth_pressure = 1.001
++ fine_tune_lr = 0.001
++
++ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+- # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+- # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+-
+- # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+-
+- # 3. Calculate repulsion forces based on artificial overlaps
+- # a) Vectorized Circle-to-circle repulsion
++
++ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+-
++ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+-
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b) Vectorized Wall repulsion
+- # Left wall (x=0)
+- overlap_l = pressured_radii - centers[:, 0]
+- forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+- # Right wall (x=1)
+- overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+- forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+- # Bottom wall (y=0)
+- overlap_b = pressured_radii - centers[:, 1]
+- forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+- # Top wall (y=1)
+- overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+- forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+-
+- # 4. Update positions with an annealing learning rate for stability
+- lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
++ # Vectorized wall repulsion
++ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
++ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
++ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
++ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
++
++ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+-
+- # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+- # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
++ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+- # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+-
+ forces = np.zeros_like(centers)
+
+- # 2. Calculate repulsion forces based on *actual* overlaps
+- # a) Vectorized Circle-to-circle repulsion
++ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+-
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+-
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+- # b) Vectorized Wall repulsion (based on actual radii)
+- # Left wall (x=0)
+- overlap_l = current_radii - centers[:, 0]
+- forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+- # Right wall (x=1)
+- overlap_r = (centers[:, 0] + current_radii) - 1.0
+- forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+- # Bottom wall (y=0)
+- overlap_b = current_radii - centers[:, 1]
+- forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+- # Top wall (y=1)
+- overlap_t = (centers[:, 1] + current_radii) - 1.0
+- forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+-
+- # 3. Update positions with a small, constant learning rate to settle
++ # Vectorized wall repulsion (based on actual radii)
++ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
++ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
++ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
++ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
++
+ centers += forces * fine_tune_lr
+-
+- # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+-
++
+ return centers
+
+-
+-def compute_max_radii(centers, iterations=500):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+- iterations: The number of iterations for the proportional scaling.
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling.
+- # This loop is more robust, checking for convergence based on minimal changes.
+- convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+-
+- for _ in range(iterations): # Iterate to stabilize radii
+- updated_in_pass = False
+- previous_radii = radii.copy()
+-
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- # If current radii would cause overlap and distance is not zero
+- if dist < 1e-9:
+- # If centers are identical, their radii must be zero.
+- if radii[i] > 0 or radii[j] > 0:
+- radii[i] = 0.0
+- radii[j] = 0.0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- # Scale both radii proportionally so they just touch
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- # Check for convergence: if no updates were made AND the max change is tiny.
+- max_delta_r = np.max(np.abs(radii - previous_radii))
+- if not updated_in_pass and max_delta_r < convergence_epsilon:
+- break
+-
+- return np.maximum(radii, 0) # Ensure no negative radii
+-
++class Individual:
++ """Represents a single candidate packing solution."""
++ def __init__(self, centers_array, n_circles):
++ self.centers = np.array(centers_array).reshape(n_circles, 2)
++ self.radii = None
++ self.fitness = -1.0
++
++ def evaluate_fitness(self, radius_iter_val):
++ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
++ self.fitness = np.sum(self.radii)
++ return self.fitness
++
++def initialize_population(n_circles, population_size):
++ """Initializes a diverse population including grid-based and random individuals."""
++ population = []
++ # 1. Add a known good starting grid (5x5 + 1)
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ grid_centers = np.zeros((n_circles, 2))
++ k = 0
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ grid_centers[k, 0] = (i + 0.5) * spacing
++ grid_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++ grid_centers[n_circles - 1] = [spacing, spacing]
++ population.append(Individual(grid_centers, n_circles))
++
++ # 2. Add perturbed versions of the grid
++ for _ in range(population_size // 5):
++ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
++ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
++
++ # 3. Fill with random individuals for diversity
++ while len(population) < population_size:
++ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
++
++ return population
++
++def select_parents(population, tournament_size):
++ """Selects two distinct parents using tournament selection."""
++ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
++ while True:
++ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
++ if p2 is not p1: return p1, p2
++
++def crossover(parent1, parent2, n_circles):
++ """Performs uniform crossover on the circle centers."""
++ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
++ return Individual(child_centers, n_circles)
++
++def mutate(individual, mutation_rate, mutation_strength, n_circles):
++ """Applies Gaussian noise to circle centers."""
++ mask = np.random.rand(n_circles) < mutation_rate
++ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
++ individual.centers[mask] += noise[mask]
++ individual.centers = np.clip(individual.centers, 0.0, 1.0)
++ return individual
++
++def construct_packing():
++ """
++ Constructs a packing of 26 circles using a Memetic Algorithm.
++ """
++ n_circles = 26
++
++ # --- Memetic Algorithm Hyperparameters ---
++ POPULATION_SIZE = 80
++ NUM_GENERATIONS = 300
++ MUTATION_RATE = 0.15
++ INITIAL_MUTATION_STRENGTH = 0.06
++ FINAL_MUTATION_STRENGTH = 0.001
++ TOURNAMENT_SIZE = 5
++ ELITISM_COUNT = 4
++ LOCAL_SEARCH_PROB = 0.7 # Probability of applying local search to a new child
++
++ FINAL_RADIUS_ITER = 500
++
++ population = initialize_population(n_circles, POPULATION_SIZE)
++
++ # Initialize and optimize the entire starting population
++ for individual in population:
++ individual.centers = hybrid_local_searcher(individual.centers.copy())
++ individual.evaluate_fitness(FINAL_RADIUS_ITER)
++
++ best_individual_overall = max(population, key=lambda ind: ind.fitness)
++
++ # --- Main MA Loop ---
++ for generation in range(NUM_GENERATIONS):
++ progress = generation / NUM_GENERATIONS
++ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
++ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
++
++ new_population = []
++
++ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
++ new_population.extend(sorted_population[:ELITISM_COUNT])
++
++ while len(new_population) < POPULATION_SIZE:
++ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
++ child = crossover(parent1, parent2, n_circles)
++ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
++
++ # --- Memetic Step: Apply Local Search ---
++ if np.random.rand() < LOCAL_SEARCH_PROB:
++ child.centers = hybrid_local_searcher(child.centers.copy())
++
++ child.evaluate_fitness(FINAL_RADIUS_ITER)
++ new_population.append(child)
++
++ population = new_population
++
++ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
++ if current_best_in_gen.fitness > best_individual_overall.fitness:
++ best_individual_overall = current_best_in_gen
++
++ # Return the best solution found
++ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ac0786fc2e05c0669ba4e412e014999703a9cfe
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/main.py
@@ -0,0 +1,248 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=120, fine_tune_iter=30, radius_sim_iter=35):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 300
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 5
+ ELITISM_COUNT = 4
+ LOCAL_SEARCH_PROB = 0.7 # Probability of applying local search to a new child
+
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(FINAL_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..0189b0ccbec58a48884aaafe7997037f915f638e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/original.py
@@ -0,0 +1,307 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.04 # Increased pressure to better 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+ num_trials_per_candidate = 3 # Number of simulations per candidate starting position.
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Expanded list to explore a wider range of starting configurations.
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95],
+ # Edge midpoints
+ [0.05, 0.5],
+ [0.5, 0.05],
+ [0.95, 0.5],
+ [0.5, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # Add a stronger perturbation to the base grid to better break symmetry and encourage rearrangement.
+ perturbation_strength = 0.025 * spacing # 2.5% of the grid spacing
+
+ # Iterate through each candidate position, running multiple trials for each to find a better optimum.
+ for extra_pos in candidate_extra_positions:
+ for _ in range(num_trials_per_candidate):
+ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+
+ # Start with a fresh copy of the base grid and perturb it
+ perturbed_grid = base_25_centers.copy()
+ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..b0b576797d393afafb7d6707a596b541764ac4f3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results
+Run 1/1 completed in 6386.46 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.2522791309031045
+ public: {'centers_str': ' centers[0] = (0.0360, 0.0340)\n centers[1] = (0.3831, 0.0705)\n centers[2] = (0.5216, 0.0621)\n centers[3] = (0.7719, 0.0612)\n centers[4] = (0.9175, 0.0828)\n centers[5] = (0.0557, 0.3681)\n centers[6] = (0.4043, 0.2559)\n centers[7] = (0.5997, 0.6066)\n centers[8] = (0.6348, 0.1934)\n centers[9] = (0.8732, 0.2921)\n centers[10] = (0.1039, 0.5205)\n centers[11] = (0.3163, 0.4643)\n centers[12] = (0.4926, 0.3617)\n centers[13] = (0.6846, 0.4460)\n centers[14] = (0.8920, 0.5295)\n centers[15] = (0.0394, 0.6722)\n centers[16] = (0.2297, 0.7169)\n centers[17] = (0.4731, 0.6131)\n centers[18] = (0.7133, 0.7049)\n centers[19] = (0.9271, 0.7104)\n centers[20] = (0.0877, 0.9146)\n centers[21] = (0.2794, 0.9333)\n centers[22] = (0.4855, 0.8644)\n centers[23] = (0.7044, 0.9227)\n centers[24] = (0.8894, 0.8893)\n centers[25] = (0.2001, 0.1899)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.2522791309031045}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/packing_viz.png
+ execution_time_mean: 6386.4589167232625
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..0bf3837c6829f6689e8f03d941fc78f9bb1e253b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.2522791309031045,
+ "public": {
+ "centers_str": " centers[0] = (0.0360, 0.0340)\n centers[1] = (0.3831, 0.0705)\n centers[2] = (0.5216, 0.0621)\n centers[3] = (0.7719, 0.0612)\n centers[4] = (0.9175, 0.0828)\n centers[5] = (0.0557, 0.3681)\n centers[6] = (0.4043, 0.2559)\n centers[7] = (0.5997, 0.6066)\n centers[8] = (0.6348, 0.1934)\n centers[9] = (0.8732, 0.2921)\n centers[10] = (0.1039, 0.5205)\n centers[11] = (0.3163, 0.4643)\n centers[12] = (0.4926, 0.3617)\n centers[13] = (0.6846, 0.4460)\n centers[14] = (0.8920, 0.5295)\n centers[15] = (0.0394, 0.6722)\n centers[16] = (0.2297, 0.7169)\n centers[17] = (0.4731, 0.6131)\n centers[18] = (0.7133, 0.7049)\n centers[19] = (0.9271, 0.7104)\n centers[20] = (0.0877, 0.9146)\n centers[21] = (0.2794, 0.9333)\n centers[22] = (0.4855, 0.8644)\n centers[23] = (0.7044, 0.9227)\n centers[24] = (0.8894, 0.8893)\n centers[25] = (0.2001, 0.1899)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.2522791309031045
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/results/packing_viz.png",
+ "execution_time_mean": 6386.4589167232625,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a0fd4eb3b464d4c06146f53c750f418b691b8710
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_89/rewrite.txt
@@ -0,0 +1,239 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=120, fine_tune_iter=30, radius_sim_iter=35):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 300
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 5
+ ELITISM_COUNT = 4
+ LOCAL_SEARCH_PROB = 0.7 # Probability of applying local search to a new child
+
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(FINAL_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..c7f7fa78b21c6f24eb402c1336296d8c6627ba0e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/edit.diff
@@ -0,0 +1,170 @@
+--- a/original.py
++++ b/original.py
+@@ -1,155 +1,154 @@
+ # EVOLVE-BLOCK-START
+ """
+ Physical simulation-based circle packing for n=26 circles.
+-This algorithm iteratively refines circle positions to maximize radii sum.
++This algorithm starts with a strong grid-based configuration and iteratively
++refines circle positions to maximize the sum of radii.
+ """
+
+ import numpy as np
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles by simulating physical repulsion forces
+- to find an optimal arrangement of centers.
++ Constructs a packing of 26 circles by starting with a high-performing
++ grid layout and then simulating physical repulsion forces to find an
++ even more optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250
+- learning_rate = 0.1
++ # Reduced learning rate for fine-tuning a good initial configuration
++ learning_rate = 0.02
+ wall_repulsion_strength = 0.5
+
+- # --- Initialization ---
+- # Start with a perturbed grid. This is a good initial guess,
+- # and the perturbation helps break symmetry and escape a rigid structure.
++ # --- Initialization (Crossover from the high-scoring constructor) ---
++ # Start with the 5x5 grid which is a known strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+- # Place the 26th circle somewhere in the middle to start.
+- centers[25] = [0.51, 0.49]
++ # Place the 26th circle in a corner to utilize that space.
++ centers[25] = [0.05, 0.05]
+
+- # Add small random noise to break grid symmetry
+- np.random.seed(42) # for reproducibility
+- centers += np.random.normal(0, 0.01, centers.shape)
+- centers = np.clip(centers, 0.01, 0.99)
++ # No random noise is added. We want to refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap = radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..789478cda723212de990bcede8fdc0eb4f963315
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/main.py
@@ -0,0 +1,154 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm starts with a strong grid-based configuration and iteratively
+refines circle positions to maximize the sum of radii.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by starting with a high-performing
+ grid layout and then simulating physical repulsion forces to find an
+ even more optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250
+ # Reduced learning rate for fine-tuning a good initial configuration
+ learning_rate = 0.02
+ wall_repulsion_strength = 0.5
+
+ # --- Initialization (Crossover from the high-scoring constructor) ---
+ # Start with the 5x5 grid which is a known strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in a corner to utilize that space.
+ centers[25] = [0.05, 0.05]
+
+ # No random noise is added. We want to refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap = radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..e44b192582bde62068817fc151f7caba8f4de343
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/original.py
@@ -0,0 +1,155 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250
+ learning_rate = 0.1
+ wall_repulsion_strength = 0.5
+
+ # --- Initialization ---
+ # Start with a perturbed grid. This is a good initial guess,
+ # and the perturbation helps break symmetry and escape a rigid structure.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle somewhere in the middle to start.
+ centers[25] = [0.51, 0.49]
+
+ # Add small random noise to break grid symmetry
+ np.random.seed(42) # for reproducibility
+ centers += np.random.normal(0, 0.01, centers.shape)
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # --- Simulation Loop ---
+ for k in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap = radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..55b6782938cb204174c2648044d9e80ec0a525c8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_9/rewrite.txt
@@ -0,0 +1,145 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm starts with a strong grid-based configuration and iteratively
+refines circle positions to maximize the sum of radii.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by starting with a high-performing
+ grid layout and then simulating physical repulsion forces to find an
+ even more optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ iterations = 250
+ # Reduced learning rate for fine-tuning a good initial configuration
+ learning_rate = 0.02
+ wall_repulsion_strength = 0.5
+
+ # --- Initialization (Crossover from the high-scoring constructor) ---
+ # Start with the 5x5 grid which is a known strong configuration.
+ centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place the 26th circle in a corner to utilize that space.
+ centers[25] = [0.05, 0.05]
+
+ # No random noise is added. We want to refine this specific strong starting point.
+
+ # --- Simulation Loop ---
+ for k in range(iterations):
+ # 1. Calculate radii for the current center configuration
+ radii = compute_max_radii(centers)
+
+ # 2. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a. Circle-to-circle repulsion forces
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+ if dist < 1e-9: continue
+
+ overlap = radii[i] + radii[j] - dist
+
+ if overlap > 0:
+ # Apply a force proportional to the overlap, along the vector between centers
+ force_magnitude = overlap
+ force_vec = (vec / dist) * force_magnitude
+ forces[i] += force_vec
+ forces[j] -= force_vec
+
+ # b. Wall repulsion forces
+ for i in range(n):
+ # Left wall
+ overlap = radii[i] - centers[i, 0]
+ if overlap > 0:
+ forces[i, 0] += wall_repulsion_strength * overlap
+ # Right wall
+ overlap = (centers[i, 0] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 0] -= wall_repulsion_strength * overlap
+ # Bottom wall
+ overlap = radii[i] - centers[i, 1]
+ if overlap > 0:
+ forces[i, 1] += wall_repulsion_strength * overlap
+ # Top wall
+ overlap = (centers[i, 1] + radii[i]) - 1.0
+ if overlap > 0:
+ forces[i, 1] -= wall_repulsion_strength * overlap
+
+ # 3. Update center positions based on forces
+ # We adjust the learning rate over time (annealing) to help the system settle
+ current_lr = learning_rate * (1.0 - (k / iterations))**2
+ centers += forces * current_lr
+
+ # 4. Enforce boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Finalization ---
+ # After the simulation, do a final radius calculation on the optimized centers
+ final_centers = centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ for _ in range(100):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and dist is not zero
+ if radii[i] + radii[j] > dist and dist > 1e-9:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..a991b065b19976b4b4e9bf44da6df4db4e1e64f6
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/edit.diff
@@ -0,0 +1,540 @@
+--- a/original.py
++++ b/original.py
+@@ -1,298 +1,258 @@
+ # EVOLVE-BLOCK-START
+-"""Constructor-based circle packing for n=26 circles"""
+-
+ import numpy as np
+
+-
+-def construct_packing():
+- """
+- Constructs a specific arrangement of 26 circles in a unit square
+- by trying multiple initial placements for the 26th circle
+- and refining each using a two-phase force-directed simulation.
+- The best resulting packing (highest sum of radii) is then returned.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- n = 26
+-
+- # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+- sim_iter = 2500 # Increased iterations for more thorough refinement.
+- learning_rate = 0.02 # A balanced learning rate for exploration.
+- wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+- initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
+- final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+- fine_tune_iter = 600 # Increased iterations for final settlement.
+- fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+- radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+- final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+-
+- # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
++# --- Core Utility: Radius Calculation ---
++# Adopted from constructor; more robust with convergence check.
++def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-8):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ """
++ n = centers.shape[0]
++ # Initial radii are limited by wall distance
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ for _ in range(max_iter):
++ updated_in_pass = False
++ previous_radii = radii.copy()
++
++ for i in range(n):
++ for j in range(i + 1, n):
++ vec = centers[i] - centers[j]
++ dist = np.linalg.norm(vec)
++
++ if dist < 1e-9:
++ if radii[i] > 0 or radii[j] > 0:
++ radii[i], radii[j] = 0.0, 0.0
++ updated_in_pass = True
++ continue
++
++ if radii[i] + radii[j] > dist:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated_in_pass = True
++
++ max_delta_r = np.max(np.abs(radii - previous_radii))
++ if not updated_in_pass and max_delta_r < convergence_epsilon:
++ break
++
++ return np.maximum(radii, 0.0)
++
++# --- NEW STRUCTURE: Encapsulated Local Searcher ---
++class HybridLocalSearcher:
++ """
++ A force-directed local search optimizer. This class encapsulates the
++ two-phase physics simulation logic, making it a reusable component.
++ """
++ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
++ wall_strength=0.8, initial_growth_pressure=1.03,
++ final_growth_pressure=1.001, fine_tune_lr=0.001,
++ radius_sim_iter=80):
++ self.sim_iter = sim_iter
++ self.fine_tune_iter = fine_tune_iter
++ self.learning_rate = learning_rate
++ self.wall_strength = wall_strength
++ self.initial_growth_pressure = initial_growth_pressure
++ self.final_growth_pressure = final_growth_pressure
++ self.fine_tune_lr = fine_tune_lr
++ self.radius_sim_iter = radius_sim_iter
++
++ def optimize(self, centers):
++ """Refines circle centers using the force-directed simulation."""
++
++ # --- Phase 1: Annealed Growth Pressure ---
++ for i_iter in range(self.sim_iter):
++ progress = i_iter / self.sim_iter
++ growth_pressure = self.final_growth_pressure + \
++ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
++
++ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
++ pressured_radii = radii * growth_pressure
++
++ forces = self._calculate_forces(centers, pressured_radii)
++
++ lr = self.learning_rate * (1.0 - progress)**2
++ centers += forces * lr
++ centers = np.clip(centers, 0.0, 1.0)
++
++ # --- Phase 2: Fine-Tuning ---
++ for _ in range(self.fine_tune_iter):
++ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
++ forces = self._calculate_forces(centers, radii)
++ centers += forces * self.fine_tune_lr
++ centers = np.clip(centers, 0.0, 1.0)
++
++ return centers
++
++ def _calculate_forces(self, centers, radii):
++ """Calculates repulsion forces from circle overlaps and wall proximity."""
++ forces = np.zeros_like(centers)
++
++ # Circle-to-circle repulsion
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ # Wall repulsion
++ forces[:, 0] += self.wall_strength * np.maximum(0, radii - centers[:, 0])
++ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + radii) - 1.0)
++ forces[:, 1] += self.wall_strength * np.maximum(0, radii - centers[:, 1])
++ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + radii) - 1.0)
++
++ return forces
++
++# --- GA Core Components ---
++class Individual:
++ """Represents a single candidate packing solution."""
++ def __init__(self, centers_array, n_circles):
++ self.centers = np.array(centers_array).reshape(n_circles, 2)
++ self.radii = None
++ self.fitness = -1.0
++
++ def evaluate_fitness(self, radius_iter_val):
++ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
++ self.fitness = np.sum(self.radii)
++ return self.fitness
++
++def initialize_population(n_circles, population_size):
++ """Initializes a diverse population, including known good starting points."""
++ population = []
++
++ # 1. Strong baseline from 5x5 grid + interstitial
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+- base_25_centers = np.zeros((n - 1, 2))
++ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+- base_25_centers[k, 0] = (i + 0.5) * spacing
+- base_25_centers[k, 1] = (j + 0.5) * spacing
++ grid_centers[k, 0] = (i + 0.5) * spacing
++ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+-
+- # Strategic candidate placements for the 26th circle.
+- # Includes interstitial points and corner-offset positions to explore diverse optima.
+- candidate_extra_positions = [
+- [spacing, spacing], # (0.2, 0.2)
+- [spacing, 0.5], # (0.2, 0.5)
+- [spacing, 1 - spacing], # (0.2, 0.8)
+- [0.5, spacing], # (0.5, 0.2)
+- [0.5, 0.5], # (0.5, 0.5) - Center
+- [0.5, 1 - spacing], # (0.5, 0.8)
+- [1 - spacing, spacing], # (0.8, 0.2)
+- [1 - spacing, 0.5], # (0.8, 0.5)
+- [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- [0.05, 0.05], # Corner offsets
+- [0.05, 0.95],
+- [0.95, 0.05],
+- [0.95, 0.95]
+- ]
+-
+- best_sum_radii = -1.0
+- best_centers_overall = None
+- best_radii_overall = None
+-
+- # Add a small perturbation to the base grid to break symmetry and encourage rearrangement.
+- perturbation_strength = 0.01 * spacing # 1% of the grid spacing
+-
+- # Iterate through each candidate position for the 26th circle
+- for extra_pos in candidate_extra_positions:
+- # Create a new 'centers' array for this trial
+- current_centers_trial = np.zeros((n, 2))
+-
+- # Start with a fresh copy of the base grid and perturb it
+- perturbed_grid = base_25_centers.copy()
+- perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+- current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+-
+- current_centers_trial[n-1] = np.array(extra_pos)
+-
+- # Refine these centers using the simulation, passing all necessary hyperparameters
+- refined_centers = refine_centers_with_simulation(
+- current_centers_trial,
+- sim_iter, learning_rate, wall_strength,
+- initial_growth_pressure, final_growth_pressure,
+- fine_tune_iter, fine_tune_lr, radius_sim_iter
+- )
+-
+- # Compute the final, precise radii for this refined configuration
+- current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+- current_sum_radii = np.sum(current_radii_final)
+-
+- # Update if this is the best packing found so far
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers_overall = refined_centers.copy()
+- best_radii_overall = current_radii_final.copy()
+-
+- # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+- if best_centers_overall is None:
+- # Revert to a safe default if no better solution was found
+- default_centers = np.zeros((n, 2))
+- default_centers[:n-1] = base_25_centers
+- default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+- default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+- return default_centers, default_radii
+- else:
+- return best_centers_overall, best_radii_overall
+-
+-
+-def refine_centers_with_simulation(
+- centers,
+- sim_iter, learning_rate, wall_strength,
+- initial_growth_pressure, final_growth_pressure,
+- fine_tune_iter, fine_tune_lr, radius_sim_iter
+-):
+- """
+- Refines circle centers using a two-phase force-directed simulation.
+- It first uses an annealed 'growth pressure' to explore the solution space,
+- then fine-tunes the positions with actual overlaps.
+-
+- Args:
+- centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+- sim_iter: Number of iterations for the main simulation phase.
+- learning_rate: Base learning rate for center updates.
+- wall_strength: Strength of repulsion from square walls.
+- initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+- final_growth_pressure: Final multiplier for radii.
+- fine_tune_iter: Number of iterations for the fine-tuning phase.
+- fine_tune_lr: Learning rate for the fine-tuning phase.
+- radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+-
+- Returns:
+- np.array: Refined circle centers.
+- """
+- n = centers.shape[0]
+-
+- # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+- for i_iter in range(sim_iter):
+- # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+- progress = i_iter / sim_iter
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+-
+- # 1. Calculate approximate radii for current positions
+- current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+-
+- # 2. Artificially inflate radii to create expansion 'pressure'
+- pressured_radii = current_radii * current_growth_pressure
+-
+- forces = np.zeros_like(centers)
+-
+- # 3. Calculate repulsion forces based on artificial overlaps
+- # a) Vectorized Circle-to-circle repulsion
+- diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+-
+- radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0
+- np.fill_diagonal(overlaps, 0)
+-
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b) Vectorized Wall repulsion
+- # Left wall (x=0)
+- overlap_l = pressured_radii - centers[:, 0]
+- forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+- # Right wall (x=1)
+- overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+- forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+- # Bottom wall (y=0)
+- overlap_b = pressured_radii - centers[:, 1]
+- forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+- # Top wall (y=1)
+- overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+- forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+-
+- # 4. Update positions with an annealing learning rate for stability
+- lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+- centers += forces * lr
+-
+- # 5. Enforce hard boundary constraints
+- centers = np.clip(centers, 0.0, 1.0)
+-
+- # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+- for _ in range(fine_tune_iter):
+- # 1. Calculate approximate radii based on actual positions
+- current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+-
+- forces = np.zeros_like(centers)
+-
+- # 2. Calculate repulsion forces based on *actual* overlaps
+- # a) Vectorized Circle-to-circle repulsion
+- diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9
+-
+- radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0
+- np.fill_diagonal(overlaps, 0)
+-
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b) Vectorized Wall repulsion (based on actual radii)
+- # Left wall (x=0)
+- overlap_l = current_radii - centers[:, 0]
+- forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+- # Right wall (x=1)
+- overlap_r = (centers[:, 0] + current_radii) - 1.0
+- forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+- # Bottom wall (y=0)
+- overlap_b = current_radii - centers[:, 1]
+- forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+- # Top wall (y=1)
+- overlap_t = (centers[:, 1] + current_radii) - 1.0
+- forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+-
+- # 3. Update positions with a small, constant learning rate to settle
+- centers += forces * fine_tune_lr
+-
+- # 4. Enforce hard boundary constraints
+- centers = np.clip(centers, 0.0, 1.0)
+-
+- return centers
+-
+-
+-def compute_max_radii(centers, iterations=500):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+- iterations: The number of iterations for the proportional scaling.
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling.
+- # This loop is more robust, checking for convergence based on minimal changes.
+- convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+-
+- for _ in range(iterations): # Iterate to stabilize radii
+- updated_in_pass = False
+- previous_radii = radii.copy()
+-
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- # If current radii would cause overlap and distance is not zero
+- if dist < 1e-9:
+- # If centers are identical, their radii must be zero.
+- if radii[i] > 0 or radii[j] > 0:
+- radii[i] = 0.0
+- radii[j] = 0.0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- # Scale both radii proportionally so they just touch
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- # Check for convergence: if no updates were made AND the max change is tiny.
+- max_delta_r = np.max(np.abs(radii - previous_radii))
+- if not updated_in_pass and max_delta_r < convergence_epsilon:
+- break
+-
+- return np.maximum(radii, 0) # Ensure no negative radii
+-
++ grid_centers[n_circles - 1] = [spacing, spacing]
++ population.append(Individual(grid_centers, n_circles))
++
++ # 2. Perturbed versions of the baseline
++ for _ in range(population_size // 10):
++ perturbed_centers = grid_centers + np.random.normal(0, 0.01, (n_circles, 2))
++ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
++
++ # 3. Random configurations for diversity
++ while len(population) < population_size:
++ random_centers = np.random.rand(n_circles, 2)
++ population.append(Individual(random_centers, n_circles))
++
++ return population
++
++def select_parents(population, tournament_size):
++ """Selects two parents using tournament selection."""
++ tournament = np.random.choice(population, tournament_size, replace=False)
++ tournament = sorted(tournament, key=lambda ind: ind.fitness, reverse=True)
++ return tournament[0], tournament[1]
++
++def crossover(parent1, parent2, n_circles):
++ """Performs uniform crossover."""
++ child_centers = np.zeros((n_circles, 2))
++ mask = np.random.rand(n_circles) < 0.5
++ child_centers[mask] = parent1.centers[mask]
++ child_centers[~mask] = parent2.centers[~mask]
++ return Individual(child_centers, n_circles)
++
++def mutate(individual, mutation_rate, mutation_strength, n_circles):
++ """Applies simple Gaussian noise mutation."""
++ mutated_centers = individual.centers.copy()
++ for i in range(n_circles):
++ if np.random.rand() < mutation_rate:
++ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
++ individual.centers = np.clip(mutated_centers, 0.0, 1.0)
++ return individual
++
++# --- Main Orchestrator: Memetic Algorithm ---
++def construct_packing():
++ """
++ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
++ a Genetic Algorithm with a powerful force-directed local search.
++ """
++ n_circles = 26
++
++ # --- Hyperparameters ---
++ POPULATION_SIZE = 80
++ NUM_GENERATIONS = 500
++ MUTATION_RATE = 0.20
++ INITIAL_MUTATION_STRENGTH = 0.05
++ FINAL_MUTATION_STRENGTH = 0.001
++ TOURNAMENT_SIZE = 5
++ ELITISM_COUNT = 5
++ LOCAL_SEARCH_PROB = 0.1 # Probability to apply the powerful local search
++
++ GA_RADIUS_ITER = 100
++ FINAL_RADIUS_ITER = 500
++
++ # --- Initialization ---
++ population = initialize_population(n_circles, POPULATION_SIZE)
++ local_searcher = HybridLocalSearcher() # Using default (tuned) params
++
++ for individual in population:
++ individual.evaluate_fitness(GA_RADIUS_ITER)
++
++ best_individual_overall = max(population, key=lambda ind: ind.fitness)
++
++ # --- Main GA Loop ---
++ for generation in range(NUM_GENERATIONS):
++ progress = generation / NUM_GENERATIONS
++ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
++ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
++
++ new_population = []
++
++ # Elitism
++ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
++ new_population.extend(sorted_population[:ELITISM_COUNT])
++
++ # Generate new individuals
++ while len(new_population) < POPULATION_SIZE:
++ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
++ child = crossover(parent1, parent2, n_circles)
++
++ # --- Hybrid Operator: Probabilistic Local Search and Simple Mutation ---
++ # 1. Probabilistically apply the powerful but expensive local search
++ if np.random.rand() < LOCAL_SEARCH_PROB:
++ child.centers = local_searcher.optimize(child.centers)
++
++ # 2. Always apply a small standard mutation for diversity
++ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
++
++ child.evaluate_fitness(GA_RADIUS_ITER)
++ new_population.append(child)
++
++ population = new_population
++
++ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
++ if current_best_in_gen.fitness > best_individual_overall.fitness:
++ best_individual_overall = current_best_in_gen
++
++ # Final high-precision evaluation
++ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
++
++ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..b78ca62826187529538f17b9d3cbdede57b253af
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/main.py
@@ -0,0 +1,258 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# --- NEW STRUCTURE: Encapsulated Local Searcher ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer. This class encapsulates the
+ two-phase physics simulation logic, making it a reusable component.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.03,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning ---
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+ def _calculate_forces(self, centers, radii):
+ """Calculates repulsion forces from circle overlaps and wall proximity."""
+ forces = np.zeros_like(centers)
+
+ # Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Wall repulsion
+ forces[:, 0] += self.wall_strength * np.maximum(0, radii - centers[:, 0])
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + radii) - 1.0)
+ forces[:, 1] += self.wall_strength * np.maximum(0, radii - centers[:, 1])
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + radii) - 1.0)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population, including known good starting points."""
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + interstitial
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Perturbed versions of the baseline
+ for _ in range(population_size // 10):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.01, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Random configurations for diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two parents using tournament selection."""
+ tournament = np.random.choice(population, tournament_size, replace=False)
+ tournament = sorted(tournament, key=lambda ind: ind.fitness, reverse=True)
+ return tournament[0], tournament[1]
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover."""
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles) < 0.5
+ child_centers[mask] = parent1.centers[mask]
+ child_centers[~mask] = parent2.centers[~mask]
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies simple Gaussian noise mutation."""
+ mutated_centers = individual.centers.copy()
+ for i in range(n_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0)
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 500
+ MUTATION_RATE = 0.20
+ INITIAL_MUTATION_STRENGTH = 0.05
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 5
+ ELITISM_COUNT = 5
+ LOCAL_SEARCH_PROB = 0.1 # Probability to apply the powerful local search
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Using default (tuned) params
+
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main GA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate new individuals
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search and Simple Mutation ---
+ # 1. Probabilistically apply the powerful but expensive local search
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers)
+
+ # 2. Always apply a small standard mutation for diversity
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Final high-precision evaluation
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..37d063913f173f566ad1e59630b3135347d80321
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/original.py
@@ -0,0 +1,298 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs a specific arrangement of 26 circles in a unit square
+ by trying multiple initial placements for the 26th circle
+ and refining each using a two-phase force-directed simulation.
+ The best resulting packing (highest sum of radii) is then returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation phases (tuned from high-performing priors) ---
+ sim_iter = 2500 # Increased iterations for more thorough refinement.
+ learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_strength = 0.8 # Strong wall repulsion to utilize boundary space.
+ initial_growth_pressure = 1.03 # Stronger initial pressure to 'unfreeze' the grid.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 600 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+ radius_sim_iter = 150 # More iterations for radii during simulation for better force accuracy.
+ final_radius_calc_iter = 500 # Iterations for final compute_max_radii (for precision)
+
+ # Initialize base for 25 circles in a dense and symmetric 5x5 grid.
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.zeros((n - 1, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_25_centers[k, 0] = (i + 0.5) * spacing
+ base_25_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points and corner-offset positions to explore diverse optima.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.05, 0.05], # Corner offsets
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = -1.0
+ best_centers_overall = None
+ best_radii_overall = None
+
+ # Add a small perturbation to the base grid to break symmetry and encourage rearrangement.
+ perturbation_strength = 0.01 * spacing # 1% of the grid spacing
+
+ # Iterate through each candidate position for the 26th circle
+ for extra_pos in candidate_extra_positions:
+ # Create a new 'centers' array for this trial
+ current_centers_trial = np.zeros((n, 2))
+
+ # Start with a fresh copy of the base grid and perturb it
+ perturbed_grid = base_25_centers.copy()
+ perturbed_grid += np.random.normal(0, perturbation_strength, (n - 1, 2))
+ current_centers_trial[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+
+ current_centers_trial[n-1] = np.array(extra_pos)
+
+ # Refine these centers using the simulation, passing all necessary hyperparameters
+ refined_centers = refine_centers_with_simulation(
+ current_centers_trial,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ # Compute the final, precise radii for this refined configuration
+ current_radii_final = compute_max_radii(refined_centers, iterations=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ # Update if this is the best packing found so far
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+ best_radii_overall = current_radii_final.copy()
+
+ # Fallback if no valid configuration was found (should ideally not happen with good candidates)
+ if best_centers_overall is None:
+ # Revert to a safe default if no better solution was found
+ default_centers = np.zeros((n, 2))
+ default_centers[:n-1] = base_25_centers
+ default_centers[n-1] = [0.5, 0.5] # Default to center for 26th circle
+ default_radii = compute_max_radii(default_centers, iterations=final_radius_calc_iter)
+ return default_centers, default_radii
+ else:
+ return best_centers_overall, best_radii_overall
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ It first uses an annealed 'growth pressure' to explore the solution space,
+ then fine-tunes the positions with actual overlaps.
+
+ Args:
+ centers: np.array of shape (n, 2) with initial (x, y) coordinates.
+ sim_iter: Number of iterations for the main simulation phase.
+ learning_rate: Base learning rate for center updates.
+ wall_strength: Strength of repulsion from square walls.
+ initial_growth_pressure: Initial multiplier for radii to create expansion pressure.
+ final_growth_pressure: Final multiplier for radii.
+ fine_tune_iter: Number of iterations for the fine-tuning phase.
+ fine_tune_lr: Learning rate for the fine-tuning phase.
+ radius_sim_iter: Number of iterations for `compute_max_radii` during simulation.
+
+ Returns:
+ np.array: Refined circle centers.
+ """
+ n = centers.shape[0]
+
+ # --- Phase 1: Refinement Simulation (with Annealed Growth Pressure) ---
+ for i_iter in range(sim_iter):
+ # Anneal growth pressure quadratically for a 'melt' then 'cool' effect
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate approximate radii for current positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ # 2. Artificially inflate radii to create expansion 'pressure'
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # 3. Calculate repulsion forces based on artificial overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion
+ # Left wall (x=0)
+ overlap_l = pressured_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + pressured_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = pressured_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + pressured_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 4. Update positions with an annealing learning rate for stability
+ lr = learning_rate * (1.0 - (i_iter / sim_iter))**2
+ centers += forces * lr
+
+ # 5. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (No Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # 1. Calculate approximate radii based on actual positions
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+
+ forces = np.zeros_like(centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps
+ # a) Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Vectorized Wall repulsion (based on actual radii)
+ # Left wall (x=0)
+ overlap_l = current_radii - centers[:, 0]
+ forces[:, 0] += wall_strength * np.maximum(0, overlap_l)
+ # Right wall (x=1)
+ overlap_r = (centers[:, 0] + current_radii) - 1.0
+ forces[:, 0] -= wall_strength * np.maximum(0, overlap_r)
+ # Bottom wall (y=0)
+ overlap_b = current_radii - centers[:, 1]
+ forces[:, 1] += wall_strength * np.maximum(0, overlap_b)
+ # Top wall (y=1)
+ overlap_t = (centers[:, 1] + current_radii) - 1.0
+ forces[:, 1] -= wall_strength * np.maximum(0, overlap_t)
+
+ # 3. Update positions with a small, constant learning rate to settle
+ centers += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+ iterations: The number of iterations for the proportional scaling.
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ # This loop is more robust, checking for convergence based on minimal changes.
+ convergence_epsilon = 1e-8 # A small threshold to check for stabilization.
+
+ for _ in range(iterations): # Iterate to stabilize radii
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9:
+ # If centers are identical, their radii must be zero.
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally so they just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b1493837a538fdfc1a0e36958c9a93f7b2217a61
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_90/rewrite.txt
@@ -0,0 +1,249 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9:
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+# --- NEW STRUCTURE: Encapsulated Local Searcher ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer. This class encapsulates the
+ two-phase physics simulation logic, making it a reusable component.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.03,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning ---
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+ def _calculate_forces(self, centers, radii):
+ """Calculates repulsion forces from circle overlaps and wall proximity."""
+ forces = np.zeros_like(centers)
+
+ # Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Wall repulsion
+ forces[:, 0] += self.wall_strength * np.maximum(0, radii - centers[:, 0])
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + radii) - 1.0)
+ forces[:, 1] += self.wall_strength * np.maximum(0, radii - centers[:, 1])
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + radii) - 1.0)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population, including known good starting points."""
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + interstitial
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Perturbed versions of the baseline
+ for _ in range(population_size // 10):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.01, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Random configurations for diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two parents using tournament selection."""
+ tournament = np.random.choice(population, tournament_size, replace=False)
+ tournament = sorted(tournament, key=lambda ind: ind.fitness, reverse=True)
+ return tournament[0], tournament[1]
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover."""
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles) < 0.5
+ child_centers[mask] = parent1.centers[mask]
+ child_centers[~mask] = parent2.centers[~mask]
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies simple Gaussian noise mutation."""
+ mutated_centers = individual.centers.copy()
+ for i in range(n_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_centers[i] += np.random.normal(0, mutation_strength, 2)
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0)
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 500
+ MUTATION_RATE = 0.20
+ INITIAL_MUTATION_STRENGTH = 0.05
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 5
+ ELITISM_COUNT = 5
+ LOCAL_SEARCH_PROB = 0.1 # Probability to apply the powerful local search
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Using default (tuned) params
+
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main GA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate new individuals
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search and Simple Mutation ---
+ # 1. Probabilistically apply the powerful but expensive local search
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers)
+
+ # 2. Always apply a small standard mutation for diversity
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Final high-precision evaluation
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..b121402a4bd86dece990d370eaa5433e6c62d1b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/edit.diff
@@ -0,0 +1,425 @@
+--- a/original.py
++++ b/original.py
+@@ -1,296 +1,274 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+-
++
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+-
++
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+-
++
+ return np.maximum(radii, 0)
+
+
+-class LocalSearchRefiner:
+- """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+- def __init__(self, config):
++class HybridLocalSearcher:
++ """
++ A powerful local search refiner based on a two-phase physical simulation.
++ This restores the successful searcher from a previous high-scoring version.
++ """
++ def __init__(self, n, config):
++ self.n = n
+ self.config = config
+
++ def _compute_radii_for_sim(self, centers):
++ """A fast radius computation for use inside the simulation loop."""
++ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
++
+ def refine(self, centers):
+- """Applies a two-stage force-directed refinement to polish a solution."""
++ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+- # --- Stage 1: Aggressive settling with subtle growth pressure ---
+- for i_agg_iter in range(self.config['ls_aggressive_iter']):
+- progress = i_agg_iter / self.config['ls_aggressive_iter']
+- current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+- (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+- (1.0 - progress)**1.5 # Cubic decay for growth pressure
+-
+- radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+- pressured_radii = radii * current_growth_pressure
+-
+- # Vectorized Force Calculation
++ # Phase 1: Main refinement with decreasing growth pressure
++ iterations = self.config['sim_iter']
++ base_lr = self.config['learning_rate']
++ wall_strength = self.config['wall_strength']
++ initial_growth_pressure = self.config['initial_growth_pressure']
++ final_growth_pressure = self.config['final_growth_pressure']
++
++ for i_iter in range(iterations):
++ progress = i_iter / iterations
++ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++ current_radii = self._compute_radii_for_sim(refined_centers)
++ pressured_radii = current_radii * current_growth_pressure
++
++ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+-
++ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+-
+- force_mags = overlaps
+- force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+- wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+- wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+-
++ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
++
+ forces = circle_forces + wall_forces
+-
+- current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+- (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+- (1.0 - progress)**2.0 # Quadratic decay for LR
+-
+- refined_centers += forces * current_lr_agg
++ lr = base_lr * (1.0 - progress)**2
++ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+- # --- Stage 2: Fine-tuning without growth pressure ---
+- for _ in range(self.config['ls_fine_tune_iter']):
+- radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+-
+- # Vectorized Force Calculation (no growth pressure)
++ # Phase 2: Fine-tuning with minimal growth pressure
++ iterations_ft = self.config['fine_tune_iter']
++ lr_ft = self.config['fine_tune_lr']
++ for _ in range(iterations_ft):
++ current_radii = self._compute_radii_for_sim(refined_centers)
++
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+-
+- radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
++ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+-
+- force_mags = overlaps
+- force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+- wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+- wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+-
++ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
++
+ forces = circle_forces + wall_forces
+-
+- refined_centers += forces * self.config['ls_fine_tune_lr']
++ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+ class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+- self.local_search_refiner = LocalSearchRefiner(config=config)
++ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+- """Initializes a diverse population with both random and grid-based individuals."""
+- num_grid_based = self.config['population_size'] // 4
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- # A much more diverse set of strategic starting points for the 26th circle.
+- strategic_points = [
+- # Primary interstitial voids of a 5x5 grid
+- [spacing, spacing], [spacing, 1 - spacing],
+- [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+- # Center of the square
+- [0.5, 0.5],
+- # Mid-edge interstitial voids
+- [spacing, 0.5], [0.5, spacing], [0.8, 0.5], [0.5, 0.8],
+- # Near-corner positions
++ """Initializes a diverse population using strategic grid-based starts and random individuals."""
++ spacing = 1.0 / 5
++ candidate_extra_positions = [
++ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
++ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+- # Inner interstitial voids
+- [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
++ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+-
+- for i in range(self.config['population_size']):
+- if i < num_grid_based:
+- centers = np.zeros((self.n, 2))
+- k = 0
+- for row in range(num_cells_side):
+- for col in range(num_cells_side):
+- centers[k, 0] = (col + 0.5) * spacing
+- centers[k, 1] = (row + 0.5) * spacing
+- k += 1
+-
+- # Apply a slightly larger perturbation to break symmetry more effectively
+- perturbation_scale = spacing * 0.025
+- centers[:25, :] += np.random.normal(0, perturbation_scale, size=(25, 2))
+-
+- # Place 26th circle in one of the strategic voids, cycling through the list
+- extra_pos = strategic_points[i % len(strategic_points)]
+- centers[25] = np.array(extra_pos) + np.random.normal(0, perturbation_scale, size=2)
+- self.population.append(np.clip(centers, 0.0, 1.0))
+- else:
+- self.population.append(np.random.rand(self.n, 2))
+-
++
++ base_centers = np.zeros((self.n - 1, 2))
++ k = 0
++ for j in range(5):
++ for i in range(5):
++ base_centers[k, 0] = (i + 0.5) * spacing
++ base_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
++
++ for i in range(num_strategic_starts):
++ centers = np.zeros((self.n, 2))
++ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
++ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
++ centers[:self.n-1] = perturbed_base_centers
++ centers[self.n-1] = np.array(candidate_extra_positions[i])
++ self.population.append(centers)
++
++ num_random_starts = self.config['population_size'] - len(self.population)
++ for _ in range(num_random_starts):
++ self.population.append(np.random.rand(self.n, 2))
++
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+-
++
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+- """
+- Performs Blend Crossover (BLX-alpha) on continuous variables.
+- Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+- """
+- # Using a small random range around 0.5 for alpha for averaging blend
+- alpha = np.random.uniform(0.4, 0.6)
+- child = alpha * p1 + (1 - alpha) * p2
+- return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+-
+- def _mutate(self, individual, strength, mutation_rate_per_circle):
++ """Performs uniform crossover, taking whole circles from either parent."""
++ child = p1.copy()
++ mask = np.random.rand(self.n) < 0.5
++ child[mask] = p2[mask]
++ return child
++
++ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+- if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
++ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+-
+- if np.random.rand() < 0.02: # 2% chance of a jump mutation
++
++ if np.random.rand() < 0.03: # Increased chance of jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+-
++
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+-
++
+ new_population = []
+-
+- # Elitism
++
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+-
+- # Anneal mutation strength
++
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+- (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+-
+- current_mutation_rate = self.config['mutation_rate_end'] + \
+- (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+- (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
++ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+-
++
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+- child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+-
+- # Memetic step: Apply local search probabilistically
++ child = self._mutate(child, mut_strength)
++
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+-
++
+ new_population.append(child)
+-
++
+ self.population = new_population
+-
+- # Final evaluation to get the best of the last generation
++
+ self._evaluate_population()
+ return self.best_solution
+
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles using a Memetic Algorithm.
++ Constructs a packing of 26 circles using a hybrid Memetic Algorithm.
+ """
+ n = 26
+ config = {
+- 'population_size': 80, # Increased population size for more diversity
+- 'generations': 400, # Increased generations for more thorough evolution
++ # MA parameters
++ 'population_size': 60,
++ 'generations': 250,
+ 'elite_count': 4,
+- 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+- 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+- 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
++ 'tournament_size': 5,
++ 'mutation_rate': 0.4,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
++ 'initial_perturbation_scale': 0.02,
++
+ # Memetic Parameters
+- 'local_search_prob': 0.15, # Probability of applying local search to a new child
+-
+- # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+- 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+- 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+- 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+- 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+- 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+- 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+-
+- 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+- 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+- 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+- 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
++ 'local_search_prob': 0.20,
++ 'ls_config': {
++ 'sim_iter': 450,
++ 'radius_sim_iter': 100,
++ 'learning_rate': 0.015,
++ 'wall_strength': 0.6,
++ 'initial_growth_pressure': 1.05,
++ 'final_growth_pressure': 1.001,
++ 'fine_tune_iter': 250,
++ 'fine_tune_lr': 0.0015,
++ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+-
+- # Final, high-precision radius calculation for the best solution
+- final_radii = compute_max_radii(best_centers, max_iter=1500)
+-
++
++ final_radii = compute_max_radii(best_centers, max_iter=2000)
++
+ return best_centers, final_radii
+-
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..805737d4dce0d1e7b7fa1d65d03f0e516d061656
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/main.py
@@ -0,0 +1,274 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This restores the successful searcher from a previous high-scoring version.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
+ spacing = 1.0 / 5
+ candidate_extra_positions = [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ num_random_starts = self.config['population_size'] - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.03: # Increased chance of jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a hybrid Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # MA parameters
+ 'population_size': 60,
+ 'generations': 250,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.4,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'initial_perturbation_scale': 0.02,
+
+ # Memetic Parameters
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+ 'sim_iter': 450,
+ 'radius_sim_iter': 100,
+ 'learning_rate': 0.015,
+ 'wall_strength': 0.6,
+ 'initial_growth_pressure': 1.05,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ final_radii = compute_max_radii(best_centers, max_iter=2000)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f66da8eee1336f7a362a62167353bcefa39a825
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/original.py
@@ -0,0 +1,296 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class LocalSearchRefiner:
+ """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
+ def __init__(self, config):
+ self.config = config
+
+ def refine(self, centers):
+ """Applies a two-stage force-directed refinement to polish a solution."""
+ refined_centers = centers.copy()
+
+ # --- Stage 1: Aggressive settling with subtle growth pressure ---
+ for i_agg_iter in range(self.config['ls_aggressive_iter']):
+ progress = i_agg_iter / self.config['ls_aggressive_iter']
+ current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
+ (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
+ (1.0 - progress)**1.5 # Cubic decay for growth pressure
+
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
+ pressured_radii = radii * current_growth_pressure
+
+ # Vectorized Force Calculation
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ current_lr_agg = self.config['ls_aggressive_lr_end'] + \
+ (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
+ (1.0 - progress)**2.0 # Quadratic decay for LR
+
+ refined_centers += forces * current_lr_agg
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Stage 2: Fine-tuning without growth pressure ---
+ for _ in range(self.config['ls_fine_tune_iter']):
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
+
+ # Vectorized Force Calculation (no growth pressure)
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_mags = overlaps
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+
+ refined_centers += forces * self.config['ls_fine_tune_lr']
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = LocalSearchRefiner(config=config)
+
+ def _initialize_population(self):
+ """Initializes a diverse population with both random and grid-based individuals."""
+ num_grid_based = self.config['population_size'] // 4
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ # A much more diverse set of strategic starting points for the 26th circle.
+ strategic_points = [
+ # Primary interstitial voids of a 5x5 grid
+ [spacing, spacing], [spacing, 1 - spacing],
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ # Center of the square
+ [0.5, 0.5],
+ # Mid-edge interstitial voids
+ [spacing, 0.5], [0.5, spacing], [0.8, 0.5], [0.5, 0.8],
+ # Near-corner positions
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ # Inner interstitial voids
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
+ ]
+
+ for i in range(self.config['population_size']):
+ if i < num_grid_based:
+ centers = np.zeros((self.n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ centers[k, 0] = (col + 0.5) * spacing
+ centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+
+ # Apply a slightly larger perturbation to break symmetry more effectively
+ perturbation_scale = spacing * 0.025
+ centers[:25, :] += np.random.normal(0, perturbation_scale, size=(25, 2))
+
+ # Place 26th circle in one of the strategic voids, cycling through the list
+ extra_pos = strategic_points[i % len(strategic_points)]
+ centers[25] = np.array(extra_pos) + np.random.normal(0, perturbation_scale, size=2)
+ self.population.append(np.clip(centers, 0.0, 1.0))
+ else:
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
+ """
+ # Using a small random range around 0.5 for alpha for averaging blend
+ alpha = np.random.uniform(0.4, 0.6)
+ child = alpha * p1 + (1 - alpha) * p2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
+
+ current_mutation_rate = self.config['mutation_rate_end'] + \
+ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
+ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
+
+ # Memetic step: Apply local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to get the best of the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ 'population_size': 80, # Increased population size for more diversity
+ 'generations': 400, # Increased generations for more thorough evolution
+ 'elite_count': 4,
+ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
+ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
+ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ # Memetic Parameters
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
+
+ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
+ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
+ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
+ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
+ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
+ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
+ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
+
+ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
+ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
+ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
+ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best solution
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2a6bfb7288bf3b22953b30a13228b09e0cd50eec
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_91/rewrite.txt
@@ -0,0 +1,265 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This restores the successful searcher from a previous high-scoring version.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
+ spacing = 1.0 / 5
+ candidate_extra_positions = [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ num_random_starts = self.config['population_size'] - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.03: # Increased chance of jump mutation
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a hybrid Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # MA parameters
+ 'population_size': 60,
+ 'generations': 250,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.4,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'initial_perturbation_scale': 0.02,
+
+ # Memetic Parameters
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+ 'sim_iter': 450,
+ 'radius_sim_iter': 100,
+ 'learning_rate': 0.015,
+ 'wall_strength': 0.6,
+ 'initial_growth_pressure': 1.05,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ final_radii = compute_max_radii(best_centers, max_iter=2000)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..b621ed8c24d95ec2a646102ed8686731aec72994
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/edit.diff
@@ -0,0 +1,384 @@
+--- a/original.py
++++ b/original.py
+@@ -1,216 +1,213 @@
+ # EVOLVE-BLOCK-START
+ """
+-Physical simulation-based circle packing for n=26 circles.
+-This algorithm iteratively refines circle positions to maximize radii sum.
+-This version refines the annealing strategy and diversifies initial start
+-positions to improve the global search and final packing quality.
++Hybrid multi-start physical simulation for n=26 circle packing.
++This algorithm blends features from multiple high-performing scripts to create a more robust optimizer.
++It uses a diverse set of initial configurations and runs a powerful two-phase force-directed
++simulation on each to find the best packing.
+ """
+
+ import numpy as np
+
++
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles by simulating physical repulsion forces
+- to find an optimal arrangement of centers.
++ Constructs an optimized packing of 26 circles by testing multiple diverse
++ initial configurations and refining each with a hybridized two-phase simulation.
++ The best resulting packing (highest sum of radii) is returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+- # --- Hyperparameters for the simulation ---
+- # Tuned parameters for a strong but controlled "explosion" and robust fine-tuning.
+- iterations = 2200 # Increased iterations for more thorough evolution.
+- learning_rate = 0.018 # Slightly reduced LR for stability.
+- wall_repulsion_strength = 0.4 # Increased wall strength to better contain the initial expansion.
+- initial_growth_pressure = 1.04 # Moderated initial pressure for a less chaotic "explosion".
+- final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+-
++ # --- Hybridized Hyperparameters (Crossover from best parents) ---
++ sim_iter = 2200 # Increased iterations for thorough refinement (from 'current').
++ learning_rate = 0.02 # Balanced learning rate (from 'inspiration').
++ wall_strength = 0.8 # Strong wall repulsion for boundary utilization (from 'inspiration' & 'memetic').
++ initial_growth_pressure = 1.04 # Aggressive initial pressure for exploration.
++ final_growth_pressure = 1.001 # Gentle final pressure for main phase tightening.
++ fine_tune_iter = 500 # Increased fine-tuning iterations (from 'current').
++ fine_tune_lr = 0.001 # Small learning rate for settling.
++ radius_sim_iter = 100 # Fast radius calculation during simulation.
++ final_radius_calc_iter = 600 # High-precision radius calculation for final result.
++
++ # --- Diverse Initial Configuration Generation ---
++ initial_configurations = []
++
++ # 1. Grid-based initializations (5x5 grid + 26th circle)
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ base_25_centers = np.array([[(i + 0.5) * spacing, (j + 0.5) * spacing] for j in range(num_cells_side) for i in range(num_cells_side)])
++
++ # Use the diverse set of candidate positions from the 'current' parent
++ candidate_extra_positions = [
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
++ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial voids
++ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6], # Central voids
++ [0.5, 0.5], # Center
++ ]
++
++ for extra_pos in candidate_extra_positions:
++ config = np.zeros((n, 2))
++ perturbed_grid = base_25_centers.copy() + np.random.normal(0, 0.005 * spacing, (n - 1, 2))
++ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
++ config[n-1] = np.array(extra_pos)
++ initial_configurations.append(config)
++
++ # 2. Add purely random configurations for broad exploration
++ for _ in range(8):
++ initial_configurations.append(np.random.rand(n, 2))
++
++ # --- Main Optimization Loop ---
+ best_sum_radii = -1.0
+- best_final_centers = None
+-
+- # Expanded candidate positions for the 26th circle to diversify initial states (Recommendation 1).
+- candidate_extra_circle_initial_positions = [
+- # Corners (deep)
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+- # Interstitial voids (corner-like)
+- [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+- # Interstitial voids (central)
+- [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
+- ]
+-
+- for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+- # --- Initialization for current trial ---
+- current_centers = np.zeros((n, 2))
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- k = 0
+- for i in range(num_cells_side):
+- for j in range(num_cells_side):
+- current_centers[k, 0] = (i + 0.5) * spacing
+- current_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+- perturbation_scale = 0.008
+- current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+-
+- current_centers[25] = initial_pos_26th_circle
+-
+- current_centers = np.clip(current_centers, 0.0, 1.0)
+- centers_for_sim = current_centers.copy()
+-
+- # --- Main Simulation Loop (Vectorized) ---
+- for sim_iter in range(iterations):
+- radii = compute_max_radii(centers_for_sim)
+-
+- # Reverted to QUADRATIC annealing for growth pressure for a fast-subsiding push.
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * \
+- (1.0 - (sim_iter / iterations))**2
+- inflated_radii = radii * current_growth_pressure
+-
+- # --- Vectorized Force Calculation ---
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_inflated_radii - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = inflated_radii - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+- overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces = circle_forces + wall_forces
+-
+- # --- Update center positions ---
+- current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+- centers_for_sim += forces * current_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- # --- Finalization for current trial ---
+- current_final_radii = compute_max_radii(centers_for_sim)
+- current_sum_radii = np.sum(current_final_radii)
++ best_centers_overall = None
++
++ for initial_centers in initial_configurations:
++ refined_centers = refine_centers_with_simulation(
++ initial_centers.copy(),
++ sim_iter, learning_rate, wall_strength,
++ initial_growth_pressure, final_growth_pressure,
++ fine_tune_iter, fine_tune_lr, radius_sim_iter
++ )
++
++ current_radii_final = compute_max_radii(refined_centers, max_iter=final_radius_calc_iter)
++ current_sum_radii = np.sum(current_radii_final)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+- best_final_centers = centers_for_sim.copy()
+-
+- # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+- fine_tune_iterations = 500 # Increased fine-tune iterations for more precise settling.
+- fine_tune_learning_rate = 0.001 # Kept fine-tuning LR.
+- finetune_growth_pressure_start = 1.0008 # A slightly stronger nudge to escape shallow local minima.
+- finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end.
+- centers_for_sim = best_final_centers.copy()
+-
+- for sim_iter_ft in range(fine_tune_iterations):
+- radii = compute_max_radii(centers_for_sim)
+-
+- # Anneal subtle growth pressure (linear decay for stability).
+- current_finetune_growth_pressure = finetune_growth_pressure_start - \
+- (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+- (sim_iter_ft / fine_tune_iterations)
+- inflated_radii_ft = radii * current_finetune_growth_pressure
+-
+- # --- Vectorized Force Calculation ---
+- diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ best_centers_overall = refined_centers.copy()
++
++ # Final high-precision evaluation on the best candidate
++ final_centers = best_centers_overall
++ final_radii = compute_max_radii(final_centers, max_iter=final_radius_calc_iter)
++
++ return final_centers, final_radii
++
++
++def refine_centers_with_simulation(
++ centers,
++ sim_iter, learning_rate, wall_strength,
++ initial_growth_pressure, final_growth_pressure,
++ fine_tune_iter, fine_tune_lr, radius_sim_iter
++):
++ """
++ Refines circle centers using a two-phase force-directed simulation.
++ Phase 1: Annealed 'growth pressure' to explore the solution space.
++ Phase 2: Fine-tuning with zero growth pressure to settle the packing.
++ """
++ # --- Phase 1: Main Simulation with Annealed Growth Pressure ---
++ for i_iter in range(sim_iter):
++ progress = i_iter / sim_iter
++ current_growth_pressure = final_growth_pressure + \
++ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++
++ # Fast radius calculation for simulation speed
++ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter)
++ pressured_radii = current_radii * current_growth_pressure
++
++ # Vectorized force calculation
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+- sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+-
+- overlaps = np.maximum(0, sum_radii_pairs - dists)
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+- with np.errstate(divide='ignore', invalid='ignore'):
+- unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+- unit_vectors[dists < 1e-9] = 0
+-
+- force_matrix = unit_vectors * overlaps[..., np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- wall_forces = np.zeros_like(centers_for_sim)
+- overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+- overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+- overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+- overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+-
+- wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+- wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+- wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+- wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+-
+- forces = circle_forces + wall_forces
+-
+- # Anneal learning rate for fine-tuning (linear decay).
+- current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+- centers_for_sim += forces * current_finetune_lr
+- centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+-
+- final_centers = centers_for_sim
+- final_radii = compute_max_radii(final_centers)
+-
+- return final_centers, final_radii
+-
+-
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This uses an iterative proportional scaling method.
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces = np.sum(force_matrix, axis=1)
++
++ # Wall repulsion forces
++ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
++ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
++ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
++ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
++
++ # Update positions with annealed learning rate
++ lr = learning_rate * (1.0 - progress)**2
++ centers += forces * lr
++ centers = np.clip(centers, 0.0, 1.0)
++
++ # --- Phase 2: Fine-Tuning Simulation (Zero Growth Pressure) ---
++ for _ in range(fine_tune_iter):
++ # Radii calculated without growth pressure to allow settling
++ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter)
++
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ dists[dists < 1e-9] = 1e-9
++
++ # Forces based on *actual* overlaps, not pressured ones
++ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces = np.sum(force_matrix, axis=1)
++
++ # Wall repulsion forces based on actual radii
++ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
++ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
++ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
++ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
++
++ # Update positions with a small, constant learning rate
++ centers += forces * fine_tune_lr
++ centers = np.clip(centers, 0.0, 1.0)
++
++ return centers
++
++
++def compute_max_radii(centers, max_iter=500, convergence_epsilon=1e-9):
++ """
++ Compute max radii for circles, using a robust iterative method with convergence check.
++ Adopted from the high-performing memetic algorithm.
+ """
+ n = centers.shape[0]
+- radii = np.ones(n)
+-
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Increased iterations for better convergence and accuracy per simulation step.
+- for _ in range(600):
++ # Efficient initialization: radii are first limited by wall distance
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ for _ in range(max_iter):
+ updated_in_pass = False
++ previous_radii = radii.copy()
++
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+- if dist < 1e-9:
+- radii[i] = 0.0
+- radii[j] = 0.0
+- updated_in_pass = True
++ if dist < 1e-9: # Handle co-located centers
++ if radii[i] > 0 or radii[j] > 0:
++ radii[i], radii[j] = 0.0, 0.0
++ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+- updated_in_pass = True
++ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+-
+- if not updated_in_pass:
++ updated_in_pass = True
++
++ # Check for convergence
++ max_delta_r = np.max(np.abs(radii - previous_radii))
++ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+-
++
+ return np.maximum(radii, 0.0)
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..fb5b30713928d06278891e3dae812599009a649b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/main.py
@@ -0,0 +1,213 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid multi-start physical simulation for n=26 circle packing.
+This algorithm blends features from multiple high-performing scripts to create a more robust optimizer.
+It uses a diverse set of initial configurations and runs a powerful two-phase force-directed
+simulation on each to find the best packing.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an optimized packing of 26 circles by testing multiple diverse
+ initial configurations and refining each with a hybridized two-phase simulation.
+ The best resulting packing (highest sum of radii) is returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hybridized Hyperparameters (Crossover from best parents) ---
+ sim_iter = 2200 # Increased iterations for thorough refinement (from 'current').
+ learning_rate = 0.02 # Balanced learning rate (from 'inspiration').
+ wall_strength = 0.8 # Strong wall repulsion for boundary utilization (from 'inspiration' & 'memetic').
+ initial_growth_pressure = 1.04 # Aggressive initial pressure for exploration.
+ final_growth_pressure = 1.001 # Gentle final pressure for main phase tightening.
+ fine_tune_iter = 500 # Increased fine-tuning iterations (from 'current').
+ fine_tune_lr = 0.001 # Small learning rate for settling.
+ radius_sim_iter = 100 # Fast radius calculation during simulation.
+ final_radius_calc_iter = 600 # High-precision radius calculation for final result.
+
+ # --- Diverse Initial Configuration Generation ---
+ initial_configurations = []
+
+ # 1. Grid-based initializations (5x5 grid + 26th circle)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.array([[(i + 0.5) * spacing, (j + 0.5) * spacing] for j in range(num_cells_side) for i in range(num_cells_side)])
+
+ # Use the diverse set of candidate positions from the 'current' parent
+ candidate_extra_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial voids
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6], # Central voids
+ [0.5, 0.5], # Center
+ ]
+
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_grid = base_25_centers.copy() + np.random.normal(0, 0.005 * spacing, (n - 1, 2))
+ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+ config[n-1] = np.array(extra_pos)
+ initial_configurations.append(config)
+
+ # 2. Add purely random configurations for broad exploration
+ for _ in range(8):
+ initial_configurations.append(np.random.rand(n, 2))
+
+ # --- Main Optimization Loop ---
+ best_sum_radii = -1.0
+ best_centers_overall = None
+
+ for initial_centers in initial_configurations:
+ refined_centers = refine_centers_with_simulation(
+ initial_centers.copy(),
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ current_radii_final = compute_max_radii(refined_centers, max_iter=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+
+ # Final high-precision evaluation on the best candidate
+ final_centers = best_centers_overall
+ final_radii = compute_max_radii(final_centers, max_iter=final_radius_calc_iter)
+
+ return final_centers, final_radii
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ Phase 1: Annealed 'growth pressure' to explore the solution space.
+ Phase 2: Fine-tuning with zero growth pressure to settle the packing.
+ """
+ # --- Phase 1: Main Simulation with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # Fast radius calculation for simulation speed
+ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculation
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces = np.sum(force_matrix, axis=1)
+
+ # Wall repulsion forces
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ # Update positions with annealed learning rate
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (Zero Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # Radii calculated without growth pressure to allow settling
+ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter)
+
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ # Forces based on *actual* overlaps, not pressured ones
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces = np.sum(force_matrix, axis=1)
+
+ # Wall repulsion forces based on actual radii
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ # Update positions with a small, constant learning rate
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, max_iter=500, convergence_epsilon=1e-9):
+ """
+ Compute max radii for circles, using a robust iterative method with convergence check.
+ Adopted from the high-performing memetic algorithm.
+ """
+ n = centers.shape[0]
+ # Efficient initialization: radii are first limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..b60f23b7bb16fd88f56276775e7843436a7dab5b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/original.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+"""
+Physical simulation-based circle packing for n=26 circles.
+This algorithm iteratively refines circle positions to maximize radii sum.
+This version refines the annealing strategy and diversifies initial start
+positions to improve the global search and final packing quality.
+"""
+
+import numpy as np
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by simulating physical repulsion forces
+ to find an optimal arrangement of centers.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hyperparameters for the simulation ---
+ # Tuned parameters for a strong but controlled "explosion" and robust fine-tuning.
+ iterations = 2200 # Increased iterations for more thorough evolution.
+ learning_rate = 0.018 # Slightly reduced LR for stability.
+ wall_repulsion_strength = 0.4 # Increased wall strength to better contain the initial expansion.
+ initial_growth_pressure = 1.04 # Moderated initial pressure for a less chaotic "explosion".
+ final_growth_pressure = 1.0001 # Maintained for precise packing at the end of the main phase.
+
+ best_sum_radii = -1.0
+ best_final_centers = None
+
+ # Expanded candidate positions for the 26th circle to diversify initial states (Recommendation 1).
+ candidate_extra_circle_initial_positions = [
+ # Corners (deep)
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ # Interstitial voids (corner-like)
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8],
+ # Interstitial voids (central)
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6],
+ ]
+
+ for initial_pos_26th_circle in candidate_extra_circle_initial_positions:
+ # --- Initialization for current trial ---
+ current_centers = np.zeros((n, 2))
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for i in range(num_cells_side):
+ for j in range(num_cells_side):
+ current_centers[k, 0] = (i + 0.5) * spacing
+ current_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Finer random perturbation to the 5x5 grid for controlled symmetry breaking.
+ perturbation_scale = 0.008
+ current_centers[:25, :] += np.random.normal(0, spacing * perturbation_scale, size=(25, 2))
+
+ current_centers[25] = initial_pos_26th_circle
+
+ current_centers = np.clip(current_centers, 0.0, 1.0)
+ centers_for_sim = current_centers.copy()
+
+ # --- Main Simulation Loop (Vectorized) ---
+ for sim_iter in range(iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Reverted to QUADRATIC annealing for growth pressure for a fast-subsiding push.
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * \
+ (1.0 - (sim_iter / iterations))**2
+ inflated_radii = radii * current_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii) - 1.0
+ overlap_bottom = inflated_radii - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # --- Update center positions ---
+ current_lr = learning_rate * (1.0 - (sim_iter / iterations))**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Finalization for current trial ---
+ current_final_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_final_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_final_centers = centers_for_sim.copy()
+
+ # --- Post-Simulation Fine-Tuning Phase (Vectorized) ---
+ fine_tune_iterations = 500 # Increased fine-tune iterations for more precise settling.
+ fine_tune_learning_rate = 0.001 # Kept fine-tuning LR.
+ finetune_growth_pressure_start = 1.0008 # A slightly stronger nudge to escape shallow local minima.
+ finetune_growth_pressure_end = 1.00001 # Almost zero growth pressure at the end.
+ centers_for_sim = best_final_centers.copy()
+
+ for sim_iter_ft in range(fine_tune_iterations):
+ radii = compute_max_radii(centers_for_sim)
+
+ # Anneal subtle growth pressure (linear decay for stability).
+ current_finetune_growth_pressure = finetune_growth_pressure_start - \
+ (finetune_growth_pressure_start - finetune_growth_pressure_end) * \
+ (sim_iter_ft / fine_tune_iterations)
+ inflated_radii_ft = radii * current_finetune_growth_pressure
+
+ # --- Vectorized Force Calculation ---
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ sum_radii_pairs = inflated_radii_ft[:, np.newaxis] + inflated_radii_ft[np.newaxis, :]
+
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
+ unit_vectors[dists < 1e-9] = 0
+
+ force_matrix = unit_vectors * overlaps[..., np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(centers_for_sim)
+ overlap_left = inflated_radii_ft - centers_for_sim[:, 0]
+ overlap_right = (centers_for_sim[:, 0] + inflated_radii_ft) - 1.0
+ overlap_bottom = inflated_radii_ft - centers_for_sim[:, 1]
+ overlap_top = (centers_for_sim[:, 1] + inflated_radii_ft) - 1.0
+
+ wall_forces[:, 0] += wall_repulsion_strength * np.maximum(0, overlap_left)
+ wall_forces[:, 0] -= wall_repulsion_strength * np.maximum(0, overlap_right)
+ wall_forces[:, 1] += wall_repulsion_strength * np.maximum(0, overlap_bottom)
+ wall_forces[:, 1] -= wall_repulsion_strength * np.maximum(0, overlap_top)
+
+ forces = circle_forces + wall_forces
+
+ # Anneal learning rate for fine-tuning (linear decay).
+ current_finetune_lr = fine_tune_learning_rate * (1.0 - (sim_iter_ft / fine_tune_iterations))
+ centers_for_sim += forces * current_finetune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ final_centers = centers_for_sim
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Increased iterations for better convergence and accuracy per simulation step.
+ for _ in range(600):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fb5b30713928d06278891e3dae812599009a649b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_92/rewrite.txt
@@ -0,0 +1,213 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid multi-start physical simulation for n=26 circle packing.
+This algorithm blends features from multiple high-performing scripts to create a more robust optimizer.
+It uses a diverse set of initial configurations and runs a powerful two-phase force-directed
+simulation on each to find the best packing.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an optimized packing of 26 circles by testing multiple diverse
+ initial configurations and refining each with a hybridized two-phase simulation.
+ The best resulting packing (highest sum of radii) is returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hybridized Hyperparameters (Crossover from best parents) ---
+ sim_iter = 2200 # Increased iterations for thorough refinement (from 'current').
+ learning_rate = 0.02 # Balanced learning rate (from 'inspiration').
+ wall_strength = 0.8 # Strong wall repulsion for boundary utilization (from 'inspiration' & 'memetic').
+ initial_growth_pressure = 1.04 # Aggressive initial pressure for exploration.
+ final_growth_pressure = 1.001 # Gentle final pressure for main phase tightening.
+ fine_tune_iter = 500 # Increased fine-tuning iterations (from 'current').
+ fine_tune_lr = 0.001 # Small learning rate for settling.
+ radius_sim_iter = 100 # Fast radius calculation during simulation.
+ final_radius_calc_iter = 600 # High-precision radius calculation for final result.
+
+ # --- Diverse Initial Configuration Generation ---
+ initial_configurations = []
+
+ # 1. Grid-based initializations (5x5 grid + 26th circle)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.array([[(i + 0.5) * spacing, (j + 0.5) * spacing] for j in range(num_cells_side) for i in range(num_cells_side)])
+
+ # Use the diverse set of candidate positions from the 'current' parent
+ candidate_extra_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial voids
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6], # Central voids
+ [0.5, 0.5], # Center
+ ]
+
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_grid = base_25_centers.copy() + np.random.normal(0, 0.005 * spacing, (n - 1, 2))
+ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+ config[n-1] = np.array(extra_pos)
+ initial_configurations.append(config)
+
+ # 2. Add purely random configurations for broad exploration
+ for _ in range(8):
+ initial_configurations.append(np.random.rand(n, 2))
+
+ # --- Main Optimization Loop ---
+ best_sum_radii = -1.0
+ best_centers_overall = None
+
+ for initial_centers in initial_configurations:
+ refined_centers = refine_centers_with_simulation(
+ initial_centers.copy(),
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ current_radii_final = compute_max_radii(refined_centers, max_iter=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+
+ # Final high-precision evaluation on the best candidate
+ final_centers = best_centers_overall
+ final_radii = compute_max_radii(final_centers, max_iter=final_radius_calc_iter)
+
+ return final_centers, final_radii
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ Phase 1: Annealed 'growth pressure' to explore the solution space.
+ Phase 2: Fine-tuning with zero growth pressure to settle the packing.
+ """
+ # --- Phase 1: Main Simulation with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # Fast radius calculation for simulation speed
+ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculation
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces = np.sum(force_matrix, axis=1)
+
+ # Wall repulsion forces
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ # Update positions with annealed learning rate
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (Zero Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # Radii calculated without growth pressure to allow settling
+ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter)
+
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ # Forces based on *actual* overlaps, not pressured ones
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces = np.sum(force_matrix, axis=1)
+
+ # Wall repulsion forces based on actual radii
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ # Update positions with a small, constant learning rate
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, max_iter=500, convergence_epsilon=1e-9):
+ """
+ Compute max radii for circles, using a robust iterative method with convergence check.
+ Adopted from the high-performing memetic algorithm.
+ """
+ n = centers.shape[0]
+ # Efficient initialization: radii are first limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..36a5281ae18366b26e8b665eba224b97cd8a1033
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..a7c2f2baa60030b3cfbeb4ac45332490bff54d9a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/edit.diff
@@ -0,0 +1,523 @@
+--- a/original.py
++++ b/original.py
+@@ -1,251 +1,308 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+-class ConfigurableHybridPacker:
+- """
+- A class-based packer encapsulating state and logic for a hybrid
+- 'initialize-and-refine' strategy. This object-oriented structure improves
+- maintainability and separates concerns, with hyperparameters managed in a
+- centralized configuration dictionary.
++def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
++ """
++ Compute maximum radii using iterative proportional scaling with a convergence check.
++ Vectorized calculation of initial radii based on wall distance.
++ """
++ n = centers.shape[0]
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ # Iterative scaling for inter-circle distances
++ for _ in range(max_iter):
++ old_radii = radii.copy()
++ updated = False
++ for i in range(n):
++ for j in range(i + 1, n):
++ dist = np.linalg.norm(centers[i] - centers[j])
++ if radii[i] + radii[j] > dist:
++ if dist < 1e-9: # Handle co-located centers
++ radii[i], radii[j] = 0.0, 0.0
++ else:
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++
++ # Check for convergence
++ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
++ break
++
++ return np.maximum(radii, 0)
++
++
++class HybridLocalSearcher:
++ """
++ A powerful local search refiner based on a two-phase physical simulation.
++ This replaces simpler force-directed approaches by incorporating a "growth pressure"
++ concept adapted from the current program's core logic.
+ """
+ def __init__(self, n, config):
+- """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+- self.centers = np.zeros((n, 2))
+- self.radii = np.zeros(n)
+-
+- def _compute_radii_for_centers(self, centers_to_eval, iterations):
+- """
+- Computes maximum radii for a given set of centers using iterative proportional scaling.
+- This is a core utility used both during and after the simulation.
+- """
+- radii = np.min([
+- centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+- centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+- ], axis=0)
+-
+- for _ in range(iterations):
+- updated = False
+- for i in range(self.n):
+- for j in range(i + 1, self.n):
+- dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+-
+- if radii[i] + radii[j] > dist:
+- if dist < 1e-9: # Handle co-located centers
+- radii[i], radii[j] = 0.0, 0.0
+- else:
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+- updated = True
+- if not updated:
+- break
+- return np.maximum(radii, 0)
+-
+- def _run_refinement_simulation(self):
+- """
+- Performs a force-directed simulation to refine circle positions.
+- This simulation uses a 'growth pressure' concept to actively
+- encourage circles to expand and fill empty space.
+- """
++
++ def _compute_radii_for_sim(self, centers):
++ """A fast radius computation for use inside the simulation loop."""
++ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
++
++ def refine(self, centers):
++ """Applies a two-phase physical simulation to refine a configuration."""
++ refined_centers = centers.copy()
++
++ # --- Phase 1: Main refinement with decreasing growth pressure ---
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+- # Anneal growth pressure quadratically from high to low.
+- # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+-
+- # 1. Calculate the maximum non-overlapping radii for the current positions.
+- current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+-
+- # 2. Artificially inflate radii to create an expansion 'pressure'.
++ # Anneal growth pressure and learning rate quadratically
++ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++ lr = base_lr * (1.0 - progress)**2
++
++ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+-
+- forces = np.zeros_like(self.centers)
+-
+- # 3. Calculate repulsion forces based on the artificial overlaps.
+- # a) Circle-to-circle repulsion (vectorized)
+- # Calculate pairwise differences and distances
+- diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+-
+- # Prevent division by zero for co-located circles
+- dists[dists < 1e-9] = 1e-9
+-
+- # Calculate pairwise radius sums and then overlaps
+- radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+- np.fill_diagonal(overlaps, 0) # No self-repulsion
+-
+- # Calculate force magnitudes and apply them along the normalized difference vectors
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+-
+- # Sum forces for each circle
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b) Wall repulsion (fully vectorized)
+- forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0]) # Left wall
+- forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0) # Right wall
+- forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1]) # Bottom wall
+- forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0) # Top wall
+-
+- # 4. Update center positions with a decaying learning rate for stability.
+- lr = base_lr * (1.0 - (i_iter / iterations))**2
+- self.centers += forces * lr
+-
+- # 5. Enforce hard boundary constraints.
+- self.centers = np.clip(self.centers, 0.0, 1.0)
+-
+- def _run_fine_tuning(self):
+- """
+- Performs a final, short simulation with no growth pressure. This allows
+- the packing to settle and resolve any minor residual overlaps from the
+- main simulation, enabling a more precise final state.
+- """
+- iterations = self.config['fine_tune_iter']
+- lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+- wall_strength = self.config['wall_strength']
+-
+- for _ in range(iterations):
+- # 1. Calculate radii for the current positions. NO growth pressure.
+- current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+-
+- forces = np.zeros_like(self.centers)
+-
+- # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+- # a) Circle-to-circle repulsion (vectorized)
+- diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
++
++ # Vectorized force calculations
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+-
+- # Overlap is based on actual radii.
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ wall_forces = np.zeros_like(refined_centers)
++ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++ refined_centers += forces * lr
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ # --- Phase 2: Fine-tuning with minimal growth pressure ---
++ iterations_ft = self.config['fine_tune_iter']
++ lr_ft = self.config['fine_tune_lr']
++ for _ in range(iterations_ft):
++ current_radii = self._compute_radii_for_sim(refined_centers)
++
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+- overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0
++ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces += np.sum(force_matrix, axis=1)
+-
+- # b) Wall repulsion (fully vectorized, based on actual radii)
+- forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+- forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+- forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+- forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+-
+- # 3. Update positions with small learning rate.
+- self.centers += forces * lr
+-
+- # 4. Enforce hard boundary constraints.
+- self.centers = np.clip(self.centers, 0.0, 1.0)
+-
+- def pack(self, initial_centers):
+- """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+- self.centers = initial_centers.copy()
+- self._run_refinement_simulation()
+- self._run_fine_tuning()
+- # Final computation of radii for the refined centers with high precision.
+- self.radii = self._compute_radii_for_centers(self.centers, self.config['final_radius_iter'])
+- return self.centers, self.radii
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ wall_forces = np.zeros_like(refined_centers)
++ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++ refined_centers += forces * lr_ft
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ return refined_centers
++
++
++class MemeticAlgorithm:
++ """
++ Combines a Genetic Algorithm for global exploration with a powerful local search meme
++ for exploitation, addressing the limitations of a pure multi-start local search.
++ """
++ def __init__(self, n, config):
++ self.n = n
++ self.config = config
++ self.population = []
++ self.fitnesses = np.array([])
++ self.best_solution = None
++ self.best_fitness = -1.0
++ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
++
++ def _initialize_population(self):
++ """
++ Initializes a diverse population using perturbed grid-based starts and random individuals.
++ The perturbation is critical for breaking symmetry and escaping the local optima of a perfect grid.
++ """
++ pop_size = self.config['population_size']
++
++ # --- Strategic Grid-Based Initialization ---
++ base_centers = np.zeros((self.n - 1, 2))
++ k = 0
++ grid_dim = 5
++ spacing = 1.0 / grid_dim
++ for j in range(grid_dim):
++ for i in range(grid_dim):
++ base_centers[k, 0] = (i + 0.5) * spacing
++ base_centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++
++ candidate_extra_positions = self.config['initial_candidates']
++ num_strategic_starts = min(len(candidate_extra_positions), pop_size // 2)
++
++ for i in range(num_strategic_starts):
++ centers = np.zeros((self.n, 2))
++ # Critical step: Add perturbation to the base grid to break symmetry
++ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
++ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
++ centers[:self.n-1] = perturbed_base_centers
++ # Add the 26th circle at a strategic point
++ centers[self.n-1] = np.array(candidate_extra_positions[i])
++ self.population.append(centers)
++
++ # --- Random Initialization ---
++ num_random_starts = pop_size - len(self.population)
++ for _ in range(num_random_starts):
++ self.population.append(np.random.rand(self.n, 2))
++
++ def _evaluate_population(self):
++ """Calculates fitness for the population and updates the best overall solution."""
++ fitnesses = [np.sum(compute_max_radii(ind)) for ind in self.population]
++ self.fitnesses = np.array(fitnesses)
++ best_idx_current_gen = np.argmax(self.fitnesses)
++
++ if self.fitnesses[best_idx_current_gen] > self.best_fitness:
++ self.best_fitness = self.fitnesses[best_idx_current_gen]
++ self.best_solution = self.population[best_idx_current_gen].copy()
++
++ def _select_parent(self):
++ """Selects a parent using tournament selection."""
++ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
++ tourn_fitnesses = self.fitnesses[tourn_indices]
++ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
++ return self.population[winner_idx]
++
++ def _crossover(self, p1, p2):
++ """Performs uniform crossover, swapping entire circles between parents."""
++ child = p1.copy()
++ mask = np.random.rand(self.n) < 0.5
++ child[mask] = p2[mask]
++ return child
++
++ def _mutate(self, individual, strength):
++ """Applies Gaussian mutation and a small chance of a large "jump" mutation."""
++ mutated_ind = individual.copy()
++ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
++ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
++ mutated_ind[mutation_mask] += noise
++
++ if np.random.rand() < self.config['jump_mutation_prob']:
++ idx_to_reset = np.random.randint(0, self.n)
++ mutated_ind[idx_to_reset] = np.random.rand(2)
++
++ return np.clip(mutated_ind, 0.0, 1.0)
++
++ def run(self):
++ """Executes the full memetic algorithm."""
++ self._initialize_population()
++
++ for gen in range(self.config['generations']):
++ self._evaluate_population()
++
++ new_population = []
++
++ # Elitism: carry over the best individuals from the current population
++ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
++ for idx in elite_indices:
++ new_population.append(self.population[idx].copy())
++
++ # Anneal mutation strength over generations
++ progress = gen / self.config['generations']
++ mut_strength = self.config['mut_strength_end'] + \
++ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
++ (1.0 - progress)**2.0
++
++ # Generate the rest of the new population through selection, crossover, mutation, and local search
++ while len(new_population) < self.config['population_size']:
++ p1 = self._select_parent()
++ p2 = self._select_parent()
++
++ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
++ child = self._mutate(child, mut_strength)
++
++ # Memetic step: Apply powerful local search probabilistically
++ if np.random.rand() < self.config['local_search_prob']:
++ child = self.local_search_refiner.refine(child)
++
++ new_population.append(child)
++
++ self.population = new_population
++
++ # Final evaluation to capture the best from the last generation
++ self._evaluate_population()
++ return self.best_solution
++
+
+ def construct_packing():
+ """
+- Main function to construct the circle packing. This implementation uses a multi-start
+- strategy with a ConfigurableHybridPacker to explore various initial configurations
+- and select the best one.
++ Constructs a packing of 26 circles by evolving a population of solutions
++ using a Memetic Algorithm.
+ """
+ n = 26
+- # Hyperparameters for the force-directed simulation
+- packer_config = {
+- 'sim_iter': 3000, # Increased iterations for thorough refinement.
+- 'radius_sim_iter': 200, # Iterations for radius calculation during simulation (speed).
+- 'final_radius_iter': 750, # Iterations for final radius calculation (precision).
+- 'learning_rate': 0.015, # Slightly adjusted learning rate.
+- 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+- 'initial_growth_pressure': 1.03, # Start with stronger pressure to 'unfreeze' the grid.
+- 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
+- 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+- 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
++ spacing = 1.0 / 5
++ config = {
++ # MA parameters
++ 'population_size': 60,
++ 'generations': 300,
++ 'elite_count': 4,
++ 'tournament_size': 5,
++ 'mutation_rate': 0.4, # Per-circle mutation probability
++ 'jump_mutation_prob': 0.02, # Chance to reset one circle's position
++ 'mut_strength_start': 0.1,
++ 'mut_strength_end': 0.001,
++ 'crossover_rate': 0.9,
++
++ # Initialization parameters
++ 'initial_perturbation_scale': 0.02, # Critical for breaking grid symmetry
++ 'initial_candidates': [
++ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
++ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
++ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
++ ],
++
++ # Memetic Parameters (Local Search)
++ 'local_search_prob': 0.15,
++ 'ls_config': {
++ 'sim_iter': 400,
++ 'radius_sim_iter': 150,
++ 'learning_rate': 0.02,
++ 'wall_strength': 0.7,
++ 'initial_growth_pressure': 1.045,
++ 'final_growth_pressure': 1.001,
++ 'fine_tune_iter': 200,
++ 'fine_tune_lr': 0.0015,
++ }
+ }
+
+- # Initial 5x5 grid setup for 25 circles
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- base_centers = np.zeros((n - 1, 2)) # 25 circles
+- k = 0
+- for j in range(num_cells_side):
+- for i in range(num_cells_side):
+- base_centers[k, 0] = (i + 0.5) * spacing
+- base_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- # Strategic candidate placements for the 26th circle.
+- # Includes interstitial points, corner-offset points, and central locations.
+- candidate_extra_positions = [
+- [spacing, spacing], # (0.2, 0.2) - primary interstitial
+- [spacing, 1 - spacing], # (0.2, 0.8)
+- [1 - spacing, spacing], # (0.8, 0.2)
+- [1 - spacing, 1 - spacing], # (0.8, 0.8)
+- [0.5, 0.5], # Center of the square
+- [spacing, 0.5], # Mid-edge interstitial
+- [0.5, spacing],
+- [1 - spacing, 0.5],
+- [0.5, 1 - spacing],
+- [0.05, 0.05], [0.05, 0.95], # Near corners
+- [0.95, 0.05], [0.95, 0.95],
+- [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Slightly more away from exact corner
+- [0.2, 0.4], [0.4, 0.2] # Additional interstitial points
+- ]
++ solver = MemeticAlgorithm(n=n, config=config)
++ best_centers = solver.run()
+
+- best_sum_radii = -1.0
+- best_centers = None
+- best_radii = None
+-
+- # Iterate through candidate positions for the 26th circle and run the full packing
+- for extra_pos in candidate_extra_positions:
+- current_initial_centers = np.zeros((n, 2))
+- current_initial_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+- current_initial_centers[n-1] = np.array(extra_pos)
+-
+- # Create a new packer instance for each initial configuration
+- packer = ConfigurableHybridPacker(n=n, config=packer_config)
+-
+- centers_candidate, radii_candidate = packer.pack(current_initial_centers)
+- current_sum_radii = np.sum(radii_candidate)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers = centers_candidate.copy()
+- best_radii = radii_candidate.copy()
+-
+- # Fallback if no valid configuration was found (should not happen with comprehensive candidates)
+- if best_centers is None:
+- # Default to a simple initial setup and run if all candidate positions fail unexpectedly
+- default_initial_centers = np.zeros((n, 2))
+- default_initial_centers[:n-1] = base_centers
+- default_initial_centers[n-1] = [0.5, 0.5] # Central placement for 26th circle
+- packer = ConfigurableHybridPacker(n=n, config=packer_config)
+- best_centers, best_radii = packer.pack(default_initial_centers)
+-
+- return best_centers, best_radii
+-
++ # Final, high-precision radius calculation for the best found solution
++ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
++
++ return best_centers, final_radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..f064cf78633a9edcc82e7ceb38bef4d405b96bce
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/main.py
@@ -0,0 +1,308 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ Vectorized calculation of initial radii based on wall distance.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iterative scaling for inter-circle distances
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This replaces simpler force-directed approaches by incorporating a "growth pressure"
+ concept adapted from the current program's core logic.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # --- Phase 1: Main refinement with decreasing growth pressure ---
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ lr = base_lr * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-tuning with minimal growth pressure ---
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """
+ Combines a Genetic Algorithm for global exploration with a powerful local search meme
+ for exploitation, addressing the limitations of a pure multi-start local search.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """
+ Initializes a diverse population using perturbed grid-based starts and random individuals.
+ The perturbation is critical for breaking symmetry and escaping the local optima of a perfect grid.
+ """
+ pop_size = self.config['population_size']
+
+ # --- Strategic Grid-Based Initialization ---
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ grid_dim = 5
+ spacing = 1.0 / grid_dim
+ for j in range(grid_dim):
+ for i in range(grid_dim):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = self.config['initial_candidates']
+ num_strategic_starts = min(len(candidate_extra_positions), pop_size // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Critical step: Add perturbation to the base grid to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Add the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # --- Random Initialization ---
+ num_random_starts = pop_size - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best overall solution."""
+ fitnesses = [np.sum(compute_max_radii(ind)) for ind in self.population]
+ self.fitnesses = np.array(fitnesses)
+ best_idx_current_gen = np.argmax(self.fitnesses)
+
+ if self.fitnesses[best_idx_current_gen] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx_current_gen]
+ self.best_solution = self.population[best_idx_current_gen].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, swapping entire circles between parents."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a small chance of a large "jump" mutation."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals from the current population
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength over generations
+ progress = gen / self.config['generations']
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - progress)**2.0
+
+ # Generate the rest of the new population through selection, crossover, mutation, and local search
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to capture the best from the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by evolving a population of solutions
+ using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+ 'population_size': 60,
+ 'generations': 300,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.4, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.02, # Chance to reset one circle's position
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+
+ # Initialization parameters
+ 'initial_perturbation_scale': 0.02, # Critical for breaking grid symmetry
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search)
+ 'local_search_prob': 0.15,
+ 'ls_config': {
+ 'sim_iter': 400,
+ 'radius_sim_iter': 150,
+ 'learning_rate': 0.02,
+ 'wall_strength': 0.7,
+ 'initial_growth_pressure': 1.045,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 200,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best found solution
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..0248cb1de05afd62ae03aeba26e0d2683d276f70
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/original.py
@@ -0,0 +1,251 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+class ConfigurableHybridPacker:
+ """
+ A class-based packer encapsulating state and logic for a hybrid
+ 'initialize-and-refine' strategy. This object-oriented structure improves
+ maintainability and separates concerns, with hyperparameters managed in a
+ centralized configuration dictionary.
+ """
+ def __init__(self, n, config):
+ """Initializes the packer with the number of circles and a config dict."""
+ self.n = n
+ self.config = config
+ self.centers = np.zeros((n, 2))
+ self.radii = np.zeros(n)
+
+ def _compute_radii_for_centers(self, centers_to_eval, iterations):
+ """
+ Computes maximum radii for a given set of centers using iterative proportional scaling.
+ This is a core utility used both during and after the simulation.
+ """
+ radii = np.min([
+ centers_to_eval[:, 0], 1 - centers_to_eval[:, 0],
+ centers_to_eval[:, 1], 1 - centers_to_eval[:, 1]
+ ], axis=0)
+
+ for _ in range(iterations):
+ updated = False
+ for i in range(self.n):
+ for j in range(i + 1, self.n):
+ dist = np.linalg.norm(centers_to_eval[i] - centers_to_eval[j])
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+ if not updated:
+ break
+ return np.maximum(radii, 0)
+
+ def _run_refinement_simulation(self):
+ """
+ Performs a force-directed simulation to refine circle positions.
+ This simulation uses a 'growth pressure' concept to actively
+ encourage circles to expand and fill empty space.
+ """
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ # Anneal growth pressure quadratically from high to low.
+ # This 'melts' the initial configuration and then lets it 'recrystallize'.
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate the maximum non-overlapping radii for the current positions.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(self.centers)
+
+ # 3. Calculate repulsion forces based on the artificial overlaps.
+ # a) Circle-to-circle repulsion (vectorized)
+ # Calculate pairwise differences and distances
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ # Prevent division by zero for co-located circles
+ dists[dists < 1e-9] = 1e-9
+
+ # Calculate pairwise radius sums and then overlaps
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ # Calculate force magnitudes and apply them along the normalized difference vectors
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+
+ # Sum forces for each circle
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized)
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 0]) # Left wall
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + pressured_radii) - 1.0) # Right wall
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - self.centers[:, 1]) # Bottom wall
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + pressured_radii) - 1.0) # Top wall
+
+ # 4. Update center positions with a decaying learning rate for stability.
+ lr = base_lr * (1.0 - (i_iter / iterations))**2
+ self.centers += forces * lr
+
+ # 5. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def _run_fine_tuning(self):
+ """
+ Performs a final, short simulation with no growth pressure. This allows
+ the packing to settle and resolve any minor residual overlaps from the
+ main simulation, enabling a more precise final state.
+ """
+ iterations = self.config['fine_tune_iter']
+ lr = self.config['fine_tune_lr'] # Using a small, constant learning rate
+ wall_strength = self.config['wall_strength']
+
+ for _ in range(iterations):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ current_radii = self._compute_radii_for_centers(self.centers, self.config['radius_sim_iter'])
+
+ forces = np.zeros_like(self.centers)
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = self.centers[:, np.newaxis, :] - self.centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ # Overlap is based on actual radii.
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b) Wall repulsion (fully vectorized, based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - self.centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (self.centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - self.centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (self.centers[:, 1] + current_radii) - 1.0)
+
+ # 3. Update positions with small learning rate.
+ self.centers += forces * lr
+
+ # 4. Enforce hard boundary constraints.
+ self.centers = np.clip(self.centers, 0.0, 1.0)
+
+ def pack(self, initial_centers):
+ """Executes the full packing pipeline: initialize, refine, fine-tune, and finalize."""
+ self.centers = initial_centers.copy()
+ self._run_refinement_simulation()
+ self._run_fine_tuning()
+ # Final computation of radii for the refined centers with high precision.
+ self.radii = self._compute_radii_for_centers(self.centers, self.config['final_radius_iter'])
+ return self.centers, self.radii
+
+def construct_packing():
+ """
+ Main function to construct the circle packing. This implementation uses a multi-start
+ strategy with a ConfigurableHybridPacker to explore various initial configurations
+ and select the best one.
+ """
+ n = 26
+ # Hyperparameters for the force-directed simulation
+ packer_config = {
+ 'sim_iter': 3000, # Increased iterations for thorough refinement.
+ 'radius_sim_iter': 200, # Iterations for radius calculation during simulation (speed).
+ 'final_radius_iter': 750, # Iterations for final radius calculation (precision).
+ 'learning_rate': 0.015, # Slightly adjusted learning rate.
+ 'wall_strength': 0.8, # Stronger walls to better utilize boundary space.
+ 'initial_growth_pressure': 1.03, # Start with stronger pressure to 'unfreeze' the grid.
+ 'final_growth_pressure': 1.0005, # End with gentler pressure to tighten the packing.
+ 'fine_tune_iter': 1000, # Increased iterations for final settlement.
+ 'fine_tune_lr': 0.001, # Small learning rate for the fine-tuning phase.
+ }
+
+ # Initial 5x5 grid setup for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_centers = np.zeros((n - 1, 2)) # 25 circles
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ # Strategic candidate placements for the 26th circle.
+ # Includes interstitial points, corner-offset points, and central locations.
+ candidate_extra_positions = [
+ [spacing, spacing], # (0.2, 0.2) - primary interstitial
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ [0.5, 0.5], # Center of the square
+ [spacing, 0.5], # Mid-edge interstitial
+ [0.5, spacing],
+ [1 - spacing, 0.5],
+ [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], # Near corners
+ [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9], # Slightly more away from exact corner
+ [0.2, 0.4], [0.4, 0.2] # Additional interstitial points
+ ]
+
+ best_sum_radii = -1.0
+ best_centers = None
+ best_radii = None
+
+ # Iterate through candidate positions for the 26th circle and run the full packing
+ for extra_pos in candidate_extra_positions:
+ current_initial_centers = np.zeros((n, 2))
+ current_initial_centers[:n-1] = base_centers.copy() # Use copy to ensure independence
+ current_initial_centers[n-1] = np.array(extra_pos)
+
+ # Create a new packer instance for each initial configuration
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+
+ centers_candidate, radii_candidate = packer.pack(current_initial_centers)
+ current_sum_radii = np.sum(radii_candidate)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_candidate.copy()
+ best_radii = radii_candidate.copy()
+
+ # Fallback if no valid configuration was found (should not happen with comprehensive candidates)
+ if best_centers is None:
+ # Default to a simple initial setup and run if all candidate positions fail unexpectedly
+ default_initial_centers = np.zeros((n, 2))
+ default_initial_centers[:n-1] = base_centers
+ default_initial_centers[n-1] = [0.5, 0.5] # Central placement for 26th circle
+ packer = ConfigurableHybridPacker(n=n, config=packer_config)
+ best_centers, best_radii = packer.pack(default_initial_centers)
+
+ return best_centers, best_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..fb186fbf9140dc9073b51016a20d724b9e607684
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results
+Run 1/1 completed in 3359.65 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.2594726096819167
+ public: {'centers_str': ' centers[0] = (0.0916, 0.0878)\n centers[1] = (0.2727, 0.0752)\n centers[2] = (0.4282, 0.0622)\n centers[3] = (0.7289, 0.0562)\n centers[4] = (0.9372, 0.0603)\n centers[5] = (0.0708, 0.2443)\n centers[6] = (0.3670, 0.5656)\n centers[7] = (0.5863, 0.0904)\n centers[8] = (0.5364, 0.2842)\n centers[9] = (0.8318, 0.7581)\n centers[10] = (0.1036, 0.4325)\n centers[11] = (0.2702, 0.4837)\n centers[12] = (0.2805, 0.2684)\n centers[13] = (0.8101, 0.5498)\n centers[14] = (0.9451, 0.4591)\n centers[15] = (0.1019, 0.6300)\n centers[16] = (0.3207, 0.7179)\n centers[17] = (0.4908, 0.5184)\n centers[18] = (0.6426, 0.7346)\n centers[19] = (0.9285, 0.6895)\n centers[20] = (0.1178, 0.8739)\n centers[21] = (0.2999, 0.9286)\n centers[22] = (0.4711, 0.9010)\n centers[23] = (0.6661, 0.9371)\n centers[24] = (0.8962, 0.8967)\n centers[25] = (0.8235, 0.2654)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.2594726096819167}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/packing_viz.png
+ execution_time_mean: 3359.654785842169
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..a5db5ed3bc54a693c78b6ab0f64f621bb5c851bf
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.2594726096819167,
+ "public": {
+ "centers_str": " centers[0] = (0.0916, 0.0878)\n centers[1] = (0.2727, 0.0752)\n centers[2] = (0.4282, 0.0622)\n centers[3] = (0.7289, 0.0562)\n centers[4] = (0.9372, 0.0603)\n centers[5] = (0.0708, 0.2443)\n centers[6] = (0.3670, 0.5656)\n centers[7] = (0.5863, 0.0904)\n centers[8] = (0.5364, 0.2842)\n centers[9] = (0.8318, 0.7581)\n centers[10] = (0.1036, 0.4325)\n centers[11] = (0.2702, 0.4837)\n centers[12] = (0.2805, 0.2684)\n centers[13] = (0.8101, 0.5498)\n centers[14] = (0.9451, 0.4591)\n centers[15] = (0.1019, 0.6300)\n centers[16] = (0.3207, 0.7179)\n centers[17] = (0.4908, 0.5184)\n centers[18] = (0.6426, 0.7346)\n centers[19] = (0.9285, 0.6895)\n centers[20] = (0.1178, 0.8739)\n centers[21] = (0.2999, 0.9286)\n centers[22] = (0.4711, 0.9010)\n centers[23] = (0.6661, 0.9371)\n centers[24] = (0.8962, 0.8967)\n centers[25] = (0.8235, 0.2654)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.2594726096819167
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/results/packing_viz.png",
+ "execution_time_mean": 3359.654785842169,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8042818a7fbd2a14d5b17b4dac0047095a459cf5
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_93/rewrite.txt
@@ -0,0 +1,299 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ Vectorized calculation of initial radii based on wall distance.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iterative scaling for inter-circle distances
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This replaces simpler force-directed approaches by incorporating a "growth pressure"
+ concept adapted from the current program's core logic.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # --- Phase 1: Main refinement with decreasing growth pressure ---
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ lr = base_lr * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-tuning with minimal growth pressure ---
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """
+ Combines a Genetic Algorithm for global exploration with a powerful local search meme
+ for exploitation, addressing the limitations of a pure multi-start local search.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """
+ Initializes a diverse population using perturbed grid-based starts and random individuals.
+ The perturbation is critical for breaking symmetry and escaping the local optima of a perfect grid.
+ """
+ pop_size = self.config['population_size']
+
+ # --- Strategic Grid-Based Initialization ---
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ grid_dim = 5
+ spacing = 1.0 / grid_dim
+ for j in range(grid_dim):
+ for i in range(grid_dim):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = self.config['initial_candidates']
+ num_strategic_starts = min(len(candidate_extra_positions), pop_size // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Critical step: Add perturbation to the base grid to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Add the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # --- Random Initialization ---
+ num_random_starts = pop_size - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best overall solution."""
+ fitnesses = [np.sum(compute_max_radii(ind)) for ind in self.population]
+ self.fitnesses = np.array(fitnesses)
+ best_idx_current_gen = np.argmax(self.fitnesses)
+
+ if self.fitnesses[best_idx_current_gen] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx_current_gen]
+ self.best_solution = self.population[best_idx_current_gen].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, swapping entire circles between parents."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a small chance of a large "jump" mutation."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals from the current population
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength over generations
+ progress = gen / self.config['generations']
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - progress)**2.0
+
+ # Generate the rest of the new population through selection, crossover, mutation, and local search
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to capture the best from the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by evolving a population of solutions
+ using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+ 'population_size': 60,
+ 'generations': 300,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.4, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.02, # Chance to reset one circle's position
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+
+ # Initialization parameters
+ 'initial_perturbation_scale': 0.02, # Critical for breaking grid symmetry
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search)
+ 'local_search_prob': 0.15,
+ 'ls_config': {
+ 'sim_iter': 400,
+ 'radius_sim_iter': 150,
+ 'learning_rate': 0.02,
+ 'wall_strength': 0.7,
+ 'initial_growth_pressure': 1.045,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 200,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best found solution
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..26df637e0d12f36d18e6b43829ff524f7c043464
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/edit.diff
@@ -0,0 +1,425 @@
+--- a/original.py
++++ b/original.py
+@@ -1,288 +1,316 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+- # Tuned for more aggressive exploration based on high-scoring programs.
+- simulation_iterations = 2000 # Double iterations for more thorough refinement.
++ simulation_iterations = 2500 # Increased iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+- wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+- initial_growth_pressure = 1.04 # Stronger initial pressure to break grid symmetry.
++ wall_repulsion_strength = 0.9 # Slightly stronger walls to better utilize boundary space.
++ initial_growth_pressure = 1.05 # Stronger initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+- fine_tune_iter = 400 # Double iterations for final settlement.
++ fine_tune_iter = 500 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+- # Iterate through candidate positions for the 26th circle to find the best one.
++ RADIUS_SIM_ITER = 120 # Iterations for `compute_radii` during simulation phases
++ FINAL_RADIUS_ITER = 600 # Iterations for final, precise `compute_radii` calculation
++
++ # --- Generate a diverse population of initial configurations ---
++ initial_configurations = []
++
++ # 1. Add configurations based on the 5x5 grid + 26th circle
++ # Expanded candidate positions for the 26th circle
++ candidate_extra_positions = [
++ [0.1, 0.1], [0.1, 0.5], [0.1, 0.9], # Interstitial-like
++ [0.5, 0.1], [0.5, 0.5], [0.5, 0.9],
++ [0.9, 0.1], [0.9, 0.5], [0.9, 0.9],
++ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Near corners
++ [0.25, 0.25], [0.25, 0.75], [0.75, 0.25], [0.75, 0.75], # Larger interstitial voids
++ ]
++ perturbation_strength_grid = 0.005 * spacing
+ for extra_pos in candidate_extra_positions:
+- current_centers = np.zeros((n, 2))
+- current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+-
+- # Add a small random perturbation to the base grid to break symmetry
+- perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
+- current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+-
+- current_centers[25] = np.array(extra_pos)
++ config = np.zeros((n, 2))
++ perturbed_grid = base_centers.copy() + np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
++ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
++ config[n-1] = np.array(extra_pos)
++ initial_configurations.append(config)
++
++ # 2. Add configurations based on a 6x4 grid (24 circles) + 2 extra circles
++ nx_6x4, ny_6x4 = 6, 4
++ spacing_x_6x4 = 1.0 / nx_6x4
++ spacing_y_6x4 = 1.0 / ny_6x4
++ base_24_centers = np.array([[(i + 0.5) * spacing_x_6x4, (j + 0.5) * spacing_y_6x4]
++ for j in range(ny_6x4) for i in range(nx_6x4)])
++
++ # Candidate positions for the remaining 2 circles
++ candidate_two_extra_positions = [
++ [[0.05, 0.05], [0.05, 0.95]],
++ [[0.5, 0.5], [0.25, 0.25]],
++ [[0.95, 0.95], [0.75, 0.75]],
++ [[0.2, 0.8], [0.8, 0.2]],
++ [[0.5, 0.1], [0.5, 0.9]], # Along a middle axis
++ [[0.1, 0.5], [0.9, 0.5]], # Along another middle axis
++ ]
++ perturbation_strength_6x4 = 0.01
++ for extra_pair in candidate_two_extra_positions:
++ config = np.zeros((n, 2))
++ perturbed_6x4 = base_24_centers.copy() + np.random.normal(0, perturbation_strength_6x4, (n - 2, 2))
++ config[:n-2] = np.clip(perturbed_6x4, 0.0, 1.0)
++ config[n-2:] = np.array(extra_pair)
++ initial_configurations.append(config)
++
++ # 3. Add purely random configurations for broad exploration
++ num_random_configs = 10
++ for _ in range(num_random_configs):
++ initial_configurations.append(np.random.rand(n, 2))
++
++ # --- Process each initial configuration and find the best result ---
++ for initial_centers in initial_configurations:
++ centers_for_sim = np.clip(initial_centers.copy(), 0.0, 1.0) # Operate on a clipped copy
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+- centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+-
+ for sim_iter in range(simulation_iterations):
+- # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+- # 1. Calculate radii for the current positions.
+- radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+-
+- # 2. Artificially inflate radii to create an expansion 'pressure'.
++ radii_for_sim = compute_radii(centers_for_sim, max_iter=RADIUS_SIM_ITER)
+ pressured_radii = radii_for_sim * current_growth_pressure
+-
+- # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+-
+- dists[dists < 1e-9] = 1e-9 # Prevent division by zero
++ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+- overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+- np.fill_diagonal(overlaps, 0) # No self-repulsion
++ overlaps[overlaps < 0] = 0.0
++ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+- forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+- forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+- forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+- forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+-
+- # 3. Update center positions based on forces
+- # Annealing learning rate: starts high, decreases over time
+- current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
++ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0])
++ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0)
++ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1])
++ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0)
++
++ current_lr = initial_learning_rate * (1.0 - progress)**2
+ centers_for_sim += forces * current_lr
+-
+- # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ # --- Fine-Tuning Loop ---
++ for _ in range(fine_tune_iter):
++ radii_for_sim = compute_radii(centers_for_sim, max_iter=RADIUS_SIM_ITER)
++ forces = np.zeros((n, 2))
++
++ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++
++ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
++ overlaps = radii_sums - dists
++ overlaps[overlaps < 0] = 0.0
++ np.fill_diagonal(overlaps, 0)
++
++ force_magnitudes = overlaps
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
++ forces += np.sum(force_matrix, axis=1)
++
++ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
++ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
++ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
++ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
++
++ centers_for_sim += forces * fine_tune_lr
++ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
++
++ current_radii = compute_radii(centers_for_sim, max_iter=FINAL_RADIUS_ITER)
++ current_sum_radii = np.sum(current_radii)
++
++ if current_sum_radii > best_sum_radii:
++ best_sum_radii = current_sum_radii
++ best_centers = centers_for_sim.copy()
++ best_radii = current_radii.copy()
++
++ if best_centers is None:
++ final_centers = np.zeros((n, 2))
++ final_centers[:25] = base_centers
++ final_centers[25] = [0.5, 0.5]
++ final_radii = compute_radii(final_centers, max_iter=FINAL_RADIUS_ITER)
++ return final_centers, final_radii
++ else:
++ return best_centers, best_radii 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+-def compute_max_radii(centers):
++def compute_radii(centers, max_iter=500, convergence_epsilon=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+- This function performs a full, highly convergent iteration.
++ This function uses an iterative proportional scaling method with a convergence check.
+
+ Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
++ centers: np.array of shape (n, 2) with (x, y) coordinates.
++ max_iter: The maximum number of iterations for the proportional scaling.
++ convergence_epsilon: A small threshold to check for stabilization.
+
+ Returns:
+- np.array of shape (n) with radius of each circle
++ np.array of shape (n) with radius of each circle.
+ """
+ n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- # Distance to borders
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Increased iterations for better convergence for final radii
+- for _ in range(500):
++ # Efficient initialization: radii are first limited by wall distance
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ # Then, limit by distance to other circles using iterative proportional scaling.
++ for _ in range(max_iter):
+ updated_in_pass = False
++ previous_radii = radii.copy()
++
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+- # If current radii would cause overlap and distance is not zero
+- if dist < 1e-9: # Handle cases where centers might be identical or very close
+- # If centers are practically the same, they cannot both have positive radius.
+- # For packing, it's safer to consider them as overlapping severely and shrink them.
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
++ if dist < 1e-9: # Handle co-located centers: if distance is zero, radii must be zero
++ if radii[i] > 0 or radii[j] > 0: # Only update if they had positive radii
++ radii[i], radii[j] = 0.0, 0.0
++ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+- # If a full pass over all pairs results in no change, radii have stabilized
+- if not updated_in_pass:
++ # Check for convergence: if no updates were made AND the max change is tiny.
++ max_delta_r = np.max(np.abs(radii - previous_radii))
++ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+-
+-
+-def compute_radii_for_simulation(centers):
+- """
+- Compute approximate maximum radii for each circle position
+- for use inside the simulation loop. Fewer iterations for performance.
+-
+- Args:
+- centers: np.array of shape (n, 2) with (x, y) coordinates
+-
+- Returns:
+- np.array of shape (n) with radius of each circle
+- """
+- n = centers.shape[0]
+- radii = np.ones(n)
+-
+- # First, limit by distance to square borders
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Then, limit by distance to other circles using iterative proportional scaling
+- # Fewer iterations for performance; high accuracy is less critical during simulation.
+- for _ in range(150):
+- updated_in_pass = False
+- for i in range(n):
+- for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9:
+- radii[i] = 0
+- radii[j] = 0
+- updated_in_pass = True
+- continue
+-
+- if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- if not updated_in_pass:
+- break
+-
+- return np.maximum(radii, 0)
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf80d5060a69257f7cbfdec9b888e345581c342b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/main.py
@@ -0,0 +1,316 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 2500 # Increased iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.9 # Slightly stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.05 # Stronger initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 500 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ RADIUS_SIM_ITER = 120 # Iterations for `compute_radii` during simulation phases
+ FINAL_RADIUS_ITER = 600 # Iterations for final, precise `compute_radii` calculation
+
+ # --- Generate a diverse population of initial configurations ---
+ initial_configurations = []
+
+ # 1. Add configurations based on the 5x5 grid + 26th circle
+ # Expanded candidate positions for the 26th circle
+ candidate_extra_positions = [
+ [0.1, 0.1], [0.1, 0.5], [0.1, 0.9], # Interstitial-like
+ [0.5, 0.1], [0.5, 0.5], [0.5, 0.9],
+ [0.9, 0.1], [0.9, 0.5], [0.9, 0.9],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Near corners
+ [0.25, 0.25], [0.25, 0.75], [0.75, 0.25], [0.75, 0.75], # Larger interstitial voids
+ ]
+ perturbation_strength_grid = 0.005 * spacing
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_grid = base_centers.copy() + np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+ config[n-1] = np.array(extra_pos)
+ initial_configurations.append(config)
+
+ # 2. Add configurations based on a 6x4 grid (24 circles) + 2 extra circles
+ nx_6x4, ny_6x4 = 6, 4
+ spacing_x_6x4 = 1.0 / nx_6x4
+ spacing_y_6x4 = 1.0 / ny_6x4
+ base_24_centers = np.array([[(i + 0.5) * spacing_x_6x4, (j + 0.5) * spacing_y_6x4]
+ for j in range(ny_6x4) for i in range(nx_6x4)])
+
+ # Candidate positions for the remaining 2 circles
+ candidate_two_extra_positions = [
+ [[0.05, 0.05], [0.05, 0.95]],
+ [[0.5, 0.5], [0.25, 0.25]],
+ [[0.95, 0.95], [0.75, 0.75]],
+ [[0.2, 0.8], [0.8, 0.2]],
+ [[0.5, 0.1], [0.5, 0.9]], # Along a middle axis
+ [[0.1, 0.5], [0.9, 0.5]], # Along another middle axis
+ ]
+ perturbation_strength_6x4 = 0.01
+ for extra_pair in candidate_two_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_6x4 = base_24_centers.copy() + np.random.normal(0, perturbation_strength_6x4, (n - 2, 2))
+ config[:n-2] = np.clip(perturbed_6x4, 0.0, 1.0)
+ config[n-2:] = np.array(extra_pair)
+ initial_configurations.append(config)
+
+ # 3. Add purely random configurations for broad exploration
+ num_random_configs = 10
+ for _ in range(num_random_configs):
+ initial_configurations.append(np.random.rand(n, 2))
+
+ # --- Process each initial configuration and find the best result ---
+ for initial_centers in initial_configurations:
+ centers_for_sim = np.clip(initial_centers.copy(), 0.0, 1.0) # Operate on a clipped copy
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ for sim_iter in range(simulation_iterations):
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ radii_for_sim = compute_radii(centers_for_sim, max_iter=RADIUS_SIM_ITER)
+ pressured_radii = radii_for_sim * current_growth_pressure
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0)
+
+ current_lr = initial_learning_rate * (1.0 - progress)**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ for _ in range(fine_tune_iter):
+ radii_for_sim = compute_radii(centers_for_sim, max_iter=RADIUS_SIM_ITER)
+ forces = np.zeros((n, 2))
+
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ centers_for_sim += forces * fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ current_radii = compute_radii(centers_for_sim, max_iter=FINAL_RADIUS_ITER)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy()
+ best_radii = current_radii.copy()
+
+ if best_centers is None:
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5]
+ final_radii = compute_radii(final_centers, max_iter=FINAL_RADIUS_ITER)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_radii(centers, max_iter=500, convergence_epsilon=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function uses an iterative proportional scaling method with a convergence check.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
+ max_iter: The maximum number of iterations for the proportional scaling.
+ convergence_epsilon: A small threshold to check for stabilization.
+
+ Returns:
+ np.array of shape (n) with radius of each circle.
+ """
+ n = centers.shape[0]
+ # Efficient initialization: radii are first limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers: if distance is zero, radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they had positive radii
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..18c11fe7ebc12532546cc96943b9d5af39e00a40
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/original.py
@@ -0,0 +1,288 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+
+ # --- Initial Placement Strategy: 5x5 grid for 25 circles ---
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side # spacing is 0.2
+
+ base_centers = np.zeros((25, 2)) # Temporarily hold 25 centers
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ base_centers[k, 0] = (i + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ base_centers[k, 1] = (j + 0.5) * spacing # e.g., 0.1, 0.3, 0.5, 0.7, 0.9
+ k += 1
+
+ # Define strategic candidate placements for the 26th circle.
+ # Combines interstitial points (from current) and corner-offset points (from prior 1.87 score program).
+ candidate_extra_positions = [
+ # Interstitial points from 5x5 grid
+ [spacing, spacing], # (0.2, 0.2)
+ [spacing, 0.5], # (0.2, 0.5)
+ [spacing, 1 - spacing], # (0.2, 0.8)
+ [0.5, spacing], # (0.5, 0.2)
+ [0.5, 0.5], # (0.5, 0.5) - Center
+ [0.5, 1 - spacing], # (0.5, 0.8)
+ [1 - spacing, spacing], # (0.8, 0.2)
+ [1 - spacing, 0.5], # (0.8, 0.5)
+ [1 - spacing, 1 - spacing], # (0.8, 0.8)
+ # Corner-offset points
+ [0.05, 0.05],
+ [0.05, 0.95],
+ [0.95, 0.05],
+ [0.95, 0.95]
+ ]
+
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration based on high-scoring programs.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # Stronger initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ # This second phase has no growth pressure. It allows the packing to settle
+ # and resolve minor overlaps, leading to a more precise final state.
+ for _ in range(fine_tune_iter):
+ # 1. Calculate radii for the current positions. NO growth pressure.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ forces = np.zeros((n, 2))
+
+ # 2. Calculate repulsion forces based on *actual* overlaps, not pressured ones.
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on actual radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ # 3. Update positions with a small, constant learning rate.
+ centers_for_sim += forces * fine_tune_lr
+
+ # 4. Enforce hard boundary constraints.
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+
+ # After both simulation and fine-tuning, use the refined centers to compute FINAL, accurate radii
+ current_radii = compute_max_radii(centers_for_sim)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy() # Store the optimized centers
+ best_radii = current_radii.copy()
+
+ # Fallback if no valid configuration was found (should not happen with good candidates)
+ if best_centers is None:
+ # This fallback uses the base 5x5 + center as a safe default
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5] # Default to center for 26th circle
+ final_radii = compute_max_radii(final_centers)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ for _ in range(150):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..92324879d75a4e766773c90952a05046c7ca2a2e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": false,
+ "error": "unmatched ')' (main.py, line 196)"
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..4db2ce90830828ed9ab3c0780eece1d10b3c47d0
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/job_log.out
@@ -0,0 +1,14 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results
+Evaluation error: unmatched ')' (main.py, line 196)
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/metrics.json
+Evaluation or Validation failed: unmatched ')' (main.py, line 196)
+Metrics:
+ combined_score: 0.0
+ execution_time_mean: 0.0
+ execution_time_std: 0.0
+ num_successful_runs: 0
+ num_valid_runs: 0
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..fec2021a6a036637c9d3049ae9778c77cef78421
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/results/metrics.json
@@ -0,0 +1,9 @@
+{
+ "combined_score": 0.0,
+ "execution_time_mean": 0.0,
+ "execution_time_std": 0.0,
+ "num_successful_runs": 0,
+ "num_valid_runs": 0,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1f027b2dd05c63a8de3f2d8425989211953373e2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_94/search_replace.txt
@@ -0,0 +1,392 @@
+
+consolidate_radii_func
+
+
+
+This edit consolidates the two `compute_max_radii` and `compute_radii_for_simulation` functions into a single, more flexible `compute_radii` function. This new function accepts `max_iter` and `convergence_epsilon` parameters, allowing it to be used for both quick approximations during simulation and precise final evaluations. This reduces code redundancy and makes it easier to manage the iteration parameters for radius calculation. The vectorized initial radius calculation is also adopted for better performance.
+
+
+
+<<<<<<< SEARCH
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function performs a full, highly convergent iteration.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Increased iterations for better convergence for final radii
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ # If current radii would cause overlap and distance is not zero
+ if dist < 1e-9: # Handle cases where centers might be identical or very close
+ # If centers are practically the same, they cannot both have positive radius.
+ # For packing, it's safer to consider them as overlapping severely and shrink them.
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # If a full pass over all pairs results in no change, radii have stabilized
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+
+
+def compute_radii_for_simulation(centers):
+ """
+ Compute approximate maximum radii for each circle position
+ for use inside the simulation loop. Fewer iterations for performance.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles using iterative proportional scaling
+ # Fewer iterations for performance; high accuracy is less critical during simulation.
+ for _ in range(150):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9:
+ radii[i] = 0
+ radii[j] = 0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0)
+=======
+def compute_radii(centers, max_iter=500, convergence_epsilon=1e-8):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This function uses an iterative proportional scaling method with a convergence check.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
+ max_iter: The maximum number of iterations for the proportional scaling.
+ convergence_epsilon: A small threshold to check for stabilization.
+
+ Returns:
+ np.array of shape (n) with radius of each circle.
+ """
+ n = centers.shape[0]
+ # Efficient initialization: radii are first limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Then, limit by distance to other circles using iterative proportional scaling.
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers: if distance is zero, radii must be zero
+ if radii[i] > 0 or radii[j] > 0: # Only update if they had positive radii
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ # Check for convergence: if no updates were made AND the max change is tiny.
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0) # Ensure no negative radii
+>>>>>>> REPLACE
+
+
+
+diversify_initial_configs_and_refine_params
+
+
+
+This edit significantly enhances the diversity of initial configurations before running the force-directed simulation. Instead of primarily relying on a 5x5 grid, it introduces:
+1. **More `candidate_extra_positions` for 5x5 base**: A wider range of strategic spots for the 26th circle based on the 5x5 grid.
+2. **6x4 Grid configurations**: It generates initial placements based on a 6x4 grid (24 circles) and then strategically places the remaining two circles in various interstitial or corner-biased positions. This explores alternative, potentially more efficient, base layouts for N=26.
+3. **Increased Random Configurations**: The number of purely random initial configurations is increased to ensure broad exploration of the search space.
+4. **Refined Simulation Parameters**: The `simulation_iterations`, `initial_growth_pressure`, and `fine_tune_iter` are slightly increased for more thorough refinement given the expanded initial search space.
+5. **Consistent `compute_radii` usage**: All calls to radius calculation functions are updated to use the consolidated `compute_radii` function with appropriate `max_iter` values for simulation (faster) and final evaluation (more precise).
+
+
+
+<<<<<<< SEARCH
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ # Tuned for more aggressive exploration based on high-scoring programs.
+ simulation_iterations = 2000 # Double iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.8 # Stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.04 # Stronger initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 400 # Double iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ # Iterate through candidate positions for the 26th circle to find the best one.
+ for extra_pos in candidate_extra_positions:
+ current_centers = np.zeros((n, 2))
+ current_centers[:25] = base_centers.copy() # Use copy to ensure independence
+
+ # Add a small random perturbation to the base grid to break symmetry
+ perturbation_strength = 0.005 * spacing # 0.5% of grid spacing
+ current_centers[:25] += np.random.normal(0, perturbation_strength, (25, 2))
+
+ current_centers[25] = np.array(extra_pos)
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ centers_for_sim = np.clip(current_centers.copy(), 0.0, 1.0) # Operate on a clipped copy for simulation
+
+ for sim_iter in range(simulation_iterations):
+ # Anneal growth pressure quadratically.
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # 1. Calculate radii for the current positions.
+ radii_for_sim = compute_radii_for_simulation(centers_for_sim)
+
+ # 2. Artificially inflate radii to create an expansion 'pressure'.
+ pressured_radii = radii_for_sim * current_growth_pressure
+
+ # 3. Calculate forces to push centers to a better configuration
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0 # Only positive overlaps exert force
+ np.fill_diagonal(overlaps, 0) # No self-repulsion
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0]) # Left
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0) # Right
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1]) # Bottom
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0) # Top
+
+ # 3. Update center positions based on forces
+ # Annealing learning rate: starts high, decreases over time
+ current_lr = initial_learning_rate * (1.0 - (sim_iter / simulation_iterations))**2
+ centers_for_sim += forces * current_lr
+
+ # 4. Enforce boundary constraints - keep centers within [0,1]
+ centers_for_sim = np.clip(centers_for_sim, 0.0,
+=======
+ best_sum_radii = 0
+ best_centers = None
+ best_radii = None
+
+ # --- Simulation Hyperparameters ---
+ simulation_iterations = 2500 # Increased iterations for more thorough refinement.
+ initial_learning_rate = 0.02 # A balanced learning rate for exploration.
+ wall_repulsion_strength = 0.9 # Slightly stronger walls to better utilize boundary space.
+ initial_growth_pressure = 1.05 # Stronger initial pressure to break grid symmetry.
+ final_growth_pressure = 1.001 # End with gentle pressure to tighten the packing.
+ fine_tune_iter = 500 # Increased iterations for final settlement.
+ fine_tune_lr = 0.001 # Small learning rate for the fine-tuning phase.
+
+ RADIUS_SIM_ITER = 120 # Iterations for `compute_radii` during simulation phases
+ FINAL_RADIUS_ITER = 600 # Iterations for final, precise `compute_radii` calculation
+
+ # --- Generate a diverse population of initial configurations ---
+ initial_configurations = []
+
+ # 1. Add configurations based on the 5x5 grid + 26th circle
+ # Expanded candidate positions for the 26th circle
+ candidate_extra_positions = [
+ [0.1, 0.1], [0.1, 0.5], [0.1, 0.9], # Interstitial-like
+ [0.5, 0.1], [0.5, 0.5], [0.5, 0.9],
+ [0.9, 0.1], [0.9, 0.5], [0.9, 0.9],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Near corners
+ [0.25, 0.25], [0.25, 0.75], [0.75, 0.25], [0.75, 0.75], # Larger interstitial voids
+ ]
+ perturbation_strength_grid = 0.005 * spacing
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_grid = base_centers.copy() + np.random.normal(0, perturbation_strength_grid, (n - 1, 2))
+ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+ config[n-1] = np.array(extra_pos)
+ initial_configurations.append(config)
+
+ # 2. Add configurations based on a 6x4 grid (24 circles) + 2 extra circles
+ nx_6x4, ny_6x4 = 6, 4
+ spacing_x_6x4 = 1.0 / nx_6x4
+ spacing_y_6x4 = 1.0 / ny_6x4
+ base_24_centers = np.array([[(i + 0.5) * spacing_x_6x4, (j + 0.5) * spacing_y_6x4]
+ for j in range(ny_6x4) for i in range(nx_6x4)])
+
+ # Candidate positions for the remaining 2 circles
+ candidate_two_extra_positions = [
+ [[0.05, 0.05], [0.05, 0.95]],
+ [[0.5, 0.5], [0.25, 0.25]],
+ [[0.95, 0.95], [0.75, 0.75]],
+ [[0.2, 0.8], [0.8, 0.2]],
+ [[0.5, 0.1], [0.5, 0.9]], # Along a middle axis
+ [[0.1, 0.5], [0.9, 0.5]], # Along another middle axis
+ ]
+ perturbation_strength_6x4 = 0.01
+ for extra_pair in candidate_two_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_6x4 = base_24_centers.copy() + np.random.normal(0, perturbation_strength_6x4, (n - 2, 2))
+ config[:n-2] = np.clip(perturbed_6x4, 0.0, 1.0)
+ config[n-2:] = np.array(extra_pair)
+ initial_configurations.append(config)
+
+ # 3. Add purely random configurations for broad exploration
+ num_random_configs = 10
+ for _ in range(num_random_configs):
+ initial_configurations.append(np.random.rand(n, 2))
+
+ # --- Process each initial configuration and find the best result ---
+ for initial_centers in initial_configurations:
+ centers_for_sim = np.clip(initial_centers.copy(), 0.0, 1.0) # Operate on a clipped copy
+
+ # --- Physical Simulation Loop (Force-Directed Relaxation with Annealed Growth Pressure) ---
+ for sim_iter in range(simulation_iterations):
+ progress = sim_iter / simulation_iterations
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ radii_for_sim = compute_radii(centers_for_sim, max_iter=RADIUS_SIM_ITER)
+ pressured_radii = radii_for_sim * current_growth_pressure
+ forces = np.zeros((n, 2))
+
+ # a) Circle-to-circle repulsion (vectorized)
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # b. Wall repulsion forces (vectorized, based on pressured radii)
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, pressured_radii - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + pressured_radii) - 1.0)
+
+ current_lr = initial_learning_rate * (1.0 - progress)**2
+ centers_for_sim += forces * current_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ # --- Fine-Tuning Loop ---
+ for _ in range(fine_tune_iter):
+ radii_for_sim = compute_radii(centers_for_sim, max_iter=RADIUS_SIM_ITER)
+ forces = np.zeros((n, 2))
+
+ diffs = centers_for_sim[:, np.newaxis, :] - centers_for_sim[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = radii_for_sim[:, np.newaxis] + radii_for_sim[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ forces[:, 0] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 0])
+ forces[:, 0] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 0] + radii_for_sim) - 1.0)
+ forces[:, 1] += wall_repulsion_strength * np.maximum(0, radii_for_sim - centers_for_sim[:, 1])
+ forces[:, 1] -= wall_repulsion_strength * np.maximum(0, (centers_for_sim[:, 1] + radii_for_sim) - 1.0)
+
+ centers_for_sim += forces * fine_tune_lr
+ centers_for_sim = np.clip(centers_for_sim, 0.0, 1.0)
+
+ current_radii = compute_radii(centers_for_sim, max_iter=FINAL_RADIUS_ITER)
+ current_sum_radii = np.sum(current_radii)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers = centers_for_sim.copy()
+ best_radii = current_radii.copy()
+
+ if best_centers is None:
+ final_centers = np.zeros((n, 2))
+ final_centers[:25] = base_centers
+ final_centers[25] = [0.5, 0.5]
+ final_radii = compute_radii(final_centers, max_iter=FINAL_RADIUS_ITER)
+ return final_centers, final_radii
+ else:
+ return best_centers, best_radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..dda89f21d15ae562e1939fa7dce703523c226835
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..a6180e81c405b368b743d1340ba41d4e902de557
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/edit.diff
@@ -0,0 +1,493 @@
+--- a/original.py
++++ b/original.py
+@@ -1,213 +1,318 @@
+ # EVOLVE-BLOCK-START
+-"""
+-Hybrid multi-start physical simulation for n=26 circle packing.
+-This algorithm blends features from multiple high-performing scripts to create a more robust optimizer.
+-It uses a diverse set of initial configurations and runs a powerful two-phase force-directed
+-simulation on each to find the best packing.
+-"""
+-
+ import numpy as np
+
+-
+-def construct_packing():
+- """
+- Constructs an optimized packing of 26 circles by testing multiple diverse
+- initial configurations and refining each with a hybridized two-phase simulation.
+- The best resulting packing (highest sum of radii) is returned.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
+- """
+- n = 26
+-
+- # --- Hybridized Hyperparameters (Crossover from best parents) ---
+- sim_iter = 2200 # Increased iterations for thorough refinement (from 'current').
+- learning_rate = 0.02 # Balanced learning rate (from 'inspiration').
+- wall_strength = 0.8 # Strong wall repulsion for boundary utilization (from 'inspiration' & 'memetic').
+- initial_growth_pressure = 1.04 # Aggressive initial pressure for exploration.
+- final_growth_pressure = 1.001 # Gentle final pressure for main phase tightening.
+- fine_tune_iter = 500 # Increased fine-tuning iterations (from 'current').
+- fine_tune_lr = 0.001 # Small learning rate for settling.
+- radius_sim_iter = 100 # Fast radius calculation during simulation.
+- final_radius_calc_iter = 600 # High-precision radius calculation for final result.
+-
+- # --- Diverse Initial Configuration Generation ---
+- initial_configurations = []
+-
+- # 1. Grid-based initializations (5x5 grid + 26th circle)
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- base_25_centers = np.array([[(i + 0.5) * spacing, (j + 0.5) * spacing] for j in range(num_cells_side) for i in range(num_cells_side)])
+-
+- # Use the diverse set of candidate positions from the 'current' parent
+- candidate_extra_positions = [
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+- [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial voids
+- [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6], # Central voids
+- [0.5, 0.5], # Center
+- ]
+-
+- for extra_pos in candidate_extra_positions:
+- config = np.zeros((n, 2))
+- perturbed_grid = base_25_centers.copy() + np.random.normal(0, 0.005 * spacing, (n - 1, 2))
+- config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+- config[n-1] = np.array(extra_pos)
+- initial_configurations.append(config)
+-
+- # 2. Add purely random configurations for broad exploration
+- for _ in range(8):
+- initial_configurations.append(np.random.rand(n, 2))
+-
+- # --- Main Optimization Loop ---
+- best_sum_radii = -1.0
+- best_centers_overall = None
+-
+- for initial_centers in initial_configurations:
+- refined_centers = refine_centers_with_simulation(
+- initial_centers.copy(),
+- sim_iter, learning_rate, wall_strength,
+- initial_growth_pressure, final_growth_pressure,
+- fine_tune_iter, fine_tune_lr, radius_sim_iter
+- )
+-
+- current_radii_final = compute_max_radii(refined_centers, max_iter=final_radius_calc_iter)
+- current_sum_radii = np.sum(current_radii_final)
+-
+- if current_sum_radii > best_sum_radii:
+- best_sum_radii = current_sum_radii
+- best_centers_overall = refined_centers.copy()
+-
+- # Final high-precision evaluation on the best candidate
+- final_centers = best_centers_overall
+- final_radii = compute_max_radii(final_centers, max_iter=final_radius_calc_iter)
+-
+- return final_centers, final_radii
+-
+-
+-def refine_centers_with_simulation(
+- centers,
+- sim_iter, learning_rate, wall_strength,
+- initial_growth_pressure, final_growth_pressure,
+- fine_tune_iter, fine_tune_lr, radius_sim_iter
+-):
+- """
+- Refines circle centers using a two-phase force-directed simulation.
+- Phase 1: Annealed 'growth pressure' to explore the solution space.
+- Phase 2: Fine-tuning with zero growth pressure to settle the packing.
+- """
+- # --- Phase 1: Main Simulation with Annealed Growth Pressure ---
+- for i_iter in range(sim_iter):
+- progress = i_iter / sim_iter
+- current_growth_pressure = final_growth_pressure + \
+- (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+-
+- # Fast radius calculation for simulation speed
+- current_radii = compute_max_radii(centers, max_iter=radius_sim_iter)
+- pressured_radii = current_radii * current_growth_pressure
+-
+- # Vectorized force calculation
+- diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- dists[dists < 1e-9] = 1e-9
+-
+- radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+- overlaps = np.maximum(0, radii_sums - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces = np.sum(force_matrix, axis=1)
+-
+- # Wall repulsion forces
+- forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+- forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+- forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+- forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+-
+- # Update positions with annealed learning rate
+- lr = learning_rate * (1.0 - progress)**2
+- centers += forces * lr
+- centers = np.clip(centers, 0.0, 1.0)
+-
+- # --- Phase 2: Fine-Tuning Simulation (Zero Growth Pressure) ---
+- for _ in range(fine_tune_iter):
+- # Radii calculated without growth pressure to allow settling
+- current_radii = compute_max_radii(centers, max_iter=radius_sim_iter)
+-
+- diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+- dists = np.linalg.norm(diffs, axis=-1)
+- dists[dists < 1e-9] = 1e-9
+-
+- # Forces based on *actual* overlaps, not pressured ones
+- radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+- overlaps = np.maximum(0, radii_sums - dists)
+- np.fill_diagonal(overlaps, 0)
+-
+- force_magnitudes = overlaps
+- force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+- forces = np.sum(force_matrix, axis=1)
+-
+- # Wall repulsion forces based on actual radii
+- forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+- forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+- forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+- forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+-
+- # Update positions with a small, constant learning rate
+- centers += forces * fine_tune_lr
+- centers = np.clip(centers, 0.0, 1.0)
+-
+- return centers
+-
+-
+-def compute_max_radii(centers, max_iter=500, convergence_epsilon=1e-9):
+- """
+- Compute max radii for circles, using a robust iterative method with convergence check.
+- Adopted from the high-performing memetic algorithm.
++# --- Core Utility: Radius Calculation ---
++# Adopted from constructor; more robust with convergence check.
++def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
++ """
++ Compute the maximum possible radii for each circle position
++ such that they don't overlap and stay within the unit square.
++ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+- # Efficient initialization: radii are first limited by wall distance
++ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9: # Handle co-located centers
++ vec = centers[i] - centers[j]
++ dist = np.linalg.norm(vec)
++
++ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+- # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+- # Check for convergence
++ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+- return np.maximum(radii, 0.0)
++ return np.maximum(radii, 0.0) # Ensure no negative radii
++
++# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
++class HybridLocalSearcher:
++ """
++ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
++ This component refines circle center positions to maximize radii sum in a local region.
++ Combines ideas from multiple high-performing programs for robust optimization.
++ """
++ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
++ wall_strength=0.8, initial_growth_pressure=1.05,
++ final_growth_pressure=1.001, fine_tune_lr=0.001,
++ radius_sim_iter=80):
++ self.sim_iter = sim_iter
++ self.fine_tune_iter = fine_tune_iter
++ self.learning_rate = learning_rate
++ self.wall_strength = wall_strength
++ self.initial_growth_pressure = initial_growth_pressure
++ self.final_growth_pressure = final_growth_pressure
++ self.fine_tune_lr = fine_tune_lr
++ self.radius_sim_iter = radius_sim_iter
++
++ def optimize(self, centers):
++ """Refines circle centers using the force-directed simulation."""
++
++ # --- Phase 1: Annealed Growth Pressure ---
++ # This phase uses artificially inflated radii to create strong repulsive forces,
++ # helping circles to escape local minima and find more open spaces.
++ for i_iter in range(self.sim_iter):
++ progress = i_iter / self.sim_iter
++ # Growth pressure anneals from initial to final value
++ growth_pressure = self.final_growth_pressure + \
++ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
++
++ # Use faster radius calculation during simulation for performance
++ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
++ pressured_radii = radii * growth_pressure
++
++ forces = self._calculate_forces(centers, pressured_radii)
++
++ # Learning rate also anneals, starting larger for exploration and shrinking for stability
++ lr = self.learning_rate * (1.0 - progress)**2
++ centers += forces * lr
++ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
++
++ # --- Phase 2: Fine-Tuning ---
++ # After the aggressive exploration, this phase settles the packing without artificial pressure,
++ # allowing for precise placement based on actual overlaps.
++ for _ in range(self.fine_tune_iter):
++ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
++ forces = self._calculate_forces(centers, radii)
++ centers += forces * self.fine_tune_lr
++ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
++
++ return centers
++
++ def _calculate_forces(self, centers, effective_radii):
++ """
++ Calculates repulsion forces from circle overlaps and wall proximity.
++ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
++ """
++ forces = np.zeros_like(centers)
++
++ # Vectorized Circle-to-circle repulsion
++ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
++ dists = np.linalg.norm(diffs, axis=-1)
++ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
++
++ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
++ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
++
++ force_magnitudes = overlaps # Force magnitude proportional to overlap
++ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
++ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
++
++ # Vectorized Wall repulsion (pushes circles away from boundaries)
++ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
++ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
++ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
++ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
++
++ return forces
++
++# --- GA Core Components ---
++class Individual:
++ """Represents a single candidate packing solution (a set of circle centers)."""
++ def __init__(self, centers_array, n_circles):
++ self.centers = np.array(centers_array).reshape(n_circles, 2)
++ self.radii = None
++ self.fitness = -1.0 # Sum of radii, initialized to a low value
++
++ def evaluate_fitness(self, radius_iter_val):
++ """Calculates the sum of radii for the current center configuration."""
++ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
++ self.fitness = np.sum(self.radii)
++ return self.fitness
++
++def initialize_population(n_circles, population_size):
++ """
++ Initializes a diverse population for the Genetic Algorithm.
++ Includes grid-based patterns (strong baselines) and purely random configurations
++ to ensure broad exploration.
++ """
++ population = []
++
++ # 1. Strong baseline from 5x5 grid + one interstitial circle
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ grid_centers_base = np.zeros((n_circles, 2))
++ k = 0
++ for j in range(num_cells_side):
++ for i in range(num_cells_side):
++ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
++ grid_centers_base[k, 0] = (i + 0.5) * spacing
++ grid_centers_base[k, 1] = (j + 0.5) * spacing
++ k += 1
++ else:
++ break
++ # Place the 26th circle at a common interstitial point
++ if k < n_circles: # For n=26, this places the 26th circle at index 25
++ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
++
++ # Add the base grid configuration
++ population.append(Individual(grid_centers_base.copy(), n_circles))
++
++ # Add several perturbed versions of the grid for more diverse starting points
++ for _ in range(population_size // 4): # Approximately 25% of the population
++ perturbed_centers = grid_centers_base + np.random.normal(0, 0.015, (n_circles, 2)) # Increased perturbation strength
++ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
++
++ # 2. Fill the rest of the population with purely random configurations for broad diversity
++ while len(population) < population_size:
++ random_centers = np.random.rand(n_circles, 2)
++ population.append(Individual(random_centers, n_circles))
++
++ return population
++
++def select_parents(population, tournament_size):
++ """Selects two distinct parents using tournament selection."""
++ # Select parent 1 by picking the fittest from a random tournament subset
++ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
++ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
++
++ # Select parent 2, ensuring it's different from parent 1
++ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
++ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
++ if len(tournament_p2_candidates) > 0:
++ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
++ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
++ parent2 = np.random.choice(population)
++ while parent2 is parent1: # Ensure parent2 is definitely different
++ parent2 = np.random.choice(population)
++ return parent1, parent2
++
++def crossover(parent1, parent2, n_circles):
++ """
++ Performs uniform crossover: for each circle, its position (x,y) is inherited
++ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
++ """
++ child_centers = np.zeros((n_circles, 2))
++ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
++ child_centers = np.where(mask, parent1.centers, parent2.centers)
++ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
++
++def mutate(individual, mutation_rate, mutation_strength, n_circles):
++ """
++ Applies Gaussian noise mutation to individual circle centers.
++ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
++ perturbed by Gaussian noise with `mutation_strength` standard deviation.
++ """
++ mutated_centers = individual.centers.copy()
++ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
++
++ # Apply Gaussian noise only to selected circles
++ if np.any(mask):
++ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
++
++ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
++ return individual
++
++# --- Main Orchestrator: Memetic Algorithm ---
++def construct_packing():
++ """
++ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
++ a Genetic Algorithm with a powerful force-directed local search.
++ This approach combines the exploration power of GA with the refinement capability
++ of local search, incorporating best practices from high-scoring prior programs.
++ """
++ n_circles = 26
++
++ # --- Hyperparameters for Memetic Algorithm ---
++ POPULATION_SIZE = 100 # Number of candidate solutions in each generation
++ NUM_GENERATIONS = 750 # Total number of evolutionary steps
++ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
++ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
++ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
++ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
++ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
++ LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
++
++ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
++ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
++
++ # --- Initialization ---
++ population = initialize_population(n_circles, POPULATION_SIZE)
++ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
++
++ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
++ # Applying the local search to all individuals in the initial population provides
++ # a strong, diverse set of locally optimized starting points for the GA.
++ # This significantly improves the quality of solutions found early on.
++ for i, individual in enumerate(population):
++ individual.centers = local_searcher.optimize(individual.centers.copy())
++ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
++
++ best_individual_overall = max(population, key=lambda ind: ind.fitness)
++
++ # --- Main Genetic Algorithm Loop ---
++ for generation in range(NUM_GENERATIONS):
++ # Anneal mutation strength: decreases over time for fine-tuning
++ progress = generation / NUM_GENERATIONS
++ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
++ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
++
++ new_population = []
++
++ # Elitism: Directly carry over the best individuals to the next generation
++ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
++ new_population.extend(sorted_population[:ELITISM_COUNT])
++
++ # Generate the rest of the new population through selection, crossover, and mutation
++ while len(new_population) < POPULATION_SIZE:
++ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
++ child = crossover(parent1, parent2, n_circles)
++
++ # --- Hybrid Operator: Probabilistic Local Search ---
++ # Apply the powerful local search to a subset of children. This is a key
++ # memetic step, allowing the GA to guide global search while local search
++ # refines promising solutions efficiently.
++ if np.random.rand() < LOCAL_SEARCH_PROB:
++ child.centers = local_searcher.optimize(child.centers.copy())
++
++ # Always apply a small standard mutation for ongoing diversity and exploration
++ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
++
++ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
++ new_population.append(child)
++
++ population = new_population # Replace old population with the new one
++
++ # Update the overall best individual found across all generations
++ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
++ if current_best_in_gen.fitness > best_individual_overall.fitness:
++ best_individual_overall = current_best_in_gen
++
++ # Optional: print progress to monitor the search
++ # if generation % (NUM_GENERATIONS // 10) == 0:
++ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
++
++ # Final high-precision evaluation of the best individual found with more iterations
++ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
++
++ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..f438d95e939a2a820dd73994040a039dd8eab456
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/main.py
@@ -0,0 +1,318 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles: # For n=26, this places the 26th circle at index 25
+ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+
+ # Add the base grid configuration
+ population.append(Individual(grid_centers_base.copy(), n_circles))
+
+ # Add several perturbed versions of the grid for more diverse starting points
+ for _ in range(population_size // 4): # Approximately 25% of the population
+ perturbed_centers = grid_centers_base + np.random.normal(0, 0.015, (n_circles, 2)) # Increased perturbation strength
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies Gaussian noise mutation to individual circle centers.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+
+ # Apply Gaussian noise only to selected circles
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 100 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 750 # Total number of evolutionary steps
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..fb5b30713928d06278891e3dae812599009a649b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/original.py
@@ -0,0 +1,213 @@
+# EVOLVE-BLOCK-START
+"""
+Hybrid multi-start physical simulation for n=26 circle packing.
+This algorithm blends features from multiple high-performing scripts to create a more robust optimizer.
+It uses a diverse set of initial configurations and runs a powerful two-phase force-directed
+simulation on each to find the best packing.
+"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Constructs an optimized packing of 26 circles by testing multiple diverse
+ initial configurations and refining each with a hybridized two-phase simulation.
+ The best resulting packing (highest sum of radii) is returned.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Hybridized Hyperparameters (Crossover from best parents) ---
+ sim_iter = 2200 # Increased iterations for thorough refinement (from 'current').
+ learning_rate = 0.02 # Balanced learning rate (from 'inspiration').
+ wall_strength = 0.8 # Strong wall repulsion for boundary utilization (from 'inspiration' & 'memetic').
+ initial_growth_pressure = 1.04 # Aggressive initial pressure for exploration.
+ final_growth_pressure = 1.001 # Gentle final pressure for main phase tightening.
+ fine_tune_iter = 500 # Increased fine-tuning iterations (from 'current').
+ fine_tune_lr = 0.001 # Small learning rate for settling.
+ radius_sim_iter = 100 # Fast radius calculation during simulation.
+ final_radius_calc_iter = 600 # High-precision radius calculation for final result.
+
+ # --- Diverse Initial Configuration Generation ---
+ initial_configurations = []
+
+ # 1. Grid-based initializations (5x5 grid + 26th circle)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ base_25_centers = np.array([[(i + 0.5) * spacing, (j + 0.5) * spacing] for j in range(num_cells_side) for i in range(num_cells_side)])
+
+ # Use the diverse set of candidate positions from the 'current' parent
+ candidate_extra_positions = [
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95], # Corners
+ [0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], # Interstitial voids
+ [0.4, 0.4], [0.4, 0.6], [0.6, 0.4], [0.6, 0.6], # Central voids
+ [0.5, 0.5], # Center
+ ]
+
+ for extra_pos in candidate_extra_positions:
+ config = np.zeros((n, 2))
+ perturbed_grid = base_25_centers.copy() + np.random.normal(0, 0.005 * spacing, (n - 1, 2))
+ config[:n-1] = np.clip(perturbed_grid, 0.0, 1.0)
+ config[n-1] = np.array(extra_pos)
+ initial_configurations.append(config)
+
+ # 2. Add purely random configurations for broad exploration
+ for _ in range(8):
+ initial_configurations.append(np.random.rand(n, 2))
+
+ # --- Main Optimization Loop ---
+ best_sum_radii = -1.0
+ best_centers_overall = None
+
+ for initial_centers in initial_configurations:
+ refined_centers = refine_centers_with_simulation(
+ initial_centers.copy(),
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+ )
+
+ current_radii_final = compute_max_radii(refined_centers, max_iter=final_radius_calc_iter)
+ current_sum_radii = np.sum(current_radii_final)
+
+ if current_sum_radii > best_sum_radii:
+ best_sum_radii = current_sum_radii
+ best_centers_overall = refined_centers.copy()
+
+ # Final high-precision evaluation on the best candidate
+ final_centers = best_centers_overall
+ final_radii = compute_max_radii(final_centers, max_iter=final_radius_calc_iter)
+
+ return final_centers, final_radii
+
+
+def refine_centers_with_simulation(
+ centers,
+ sim_iter, learning_rate, wall_strength,
+ initial_growth_pressure, final_growth_pressure,
+ fine_tune_iter, fine_tune_lr, radius_sim_iter
+):
+ """
+ Refines circle centers using a two-phase force-directed simulation.
+ Phase 1: Annealed 'growth pressure' to explore the solution space.
+ Phase 2: Fine-tuning with zero growth pressure to settle the packing.
+ """
+ # --- Phase 1: Main Simulation with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ # Fast radius calculation for simulation speed
+ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculation
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces = np.sum(force_matrix, axis=1)
+
+ # Wall repulsion forces
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ # Update positions with annealed learning rate
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning Simulation (Zero Growth Pressure) ---
+ for _ in range(fine_tune_iter):
+ # Radii calculated without growth pressure to allow settling
+ current_radii = compute_max_radii(centers, max_iter=radius_sim_iter)
+
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9
+
+ # Forces based on *actual* overlaps, not pressured ones
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces = np.sum(force_matrix, axis=1)
+
+ # Wall repulsion forces based on actual radii
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ # Update positions with a small, constant learning rate
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+
+def compute_max_radii(centers, max_iter=500, convergence_epsilon=1e-9):
+ """
+ Compute max radii for circles, using a robust iterative method with convergence check.
+ Adopted from the high-performing memetic algorithm.
+ """
+ n = centers.shape[0]
+ # Efficient initialization: radii are first limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally to just touch
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..989088c47bde7e7ecdd084f13f5f169c09b8baab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results
+Run 1/1 completed in 10647.23 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.3039102482480134
+ public: {'centers_str': ' centers[0] = (0.0908, 0.0911)\n centers[1] = (0.5446, 0.5477)\n centers[2] = (0.5640, 0.0643)\n centers[3] = (0.7044, 0.0849)\n centers[4] = (0.8920, 0.1074)\n centers[5] = (0.0952, 0.3180)\n centers[6] = (0.2833, 0.4205)\n centers[7] = (0.5842, 0.2953)\n centers[8] = (0.7397, 0.3751)\n centers[9] = (0.9101, 0.3038)\n centers[10] = (0.0910, 0.5081)\n centers[11] = (0.3701, 0.5418)\n centers[12] = (0.4690, 0.4326)\n centers[13] = (0.6176, 0.5057)\n centers[14] = (0.8956, 0.4985)\n centers[15] = (0.0703, 0.6989)\n centers[16] = (0.2691, 0.6837)\n centers[17] = (0.4867, 0.6269)\n centers[18] = (0.7221, 0.6775)\n centers[19] = (0.9283, 0.7417)\n centers[20] = (0.0953, 0.8687)\n centers[21] = (0.2804, 0.9063)\n centers[22] = (0.5072, 0.8626)\n centers[23] = (0.7281, 0.9113)\n centers[24] = (0.9084, 0.9037)\n centers[25] = (0.3493, 0.1882)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.3039102482480134}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/packing_viz.png
+ execution_time_mean: 10647.228168863803
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..98ac0ee9197f0e981d14ced96b1b5417b15ec789
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.3039102482480134,
+ "public": {
+ "centers_str": " centers[0] = (0.0908, 0.0911)\n centers[1] = (0.5446, 0.5477)\n centers[2] = (0.5640, 0.0643)\n centers[3] = (0.7044, 0.0849)\n centers[4] = (0.8920, 0.1074)\n centers[5] = (0.0952, 0.3180)\n centers[6] = (0.2833, 0.4205)\n centers[7] = (0.5842, 0.2953)\n centers[8] = (0.7397, 0.3751)\n centers[9] = (0.9101, 0.3038)\n centers[10] = (0.0910, 0.5081)\n centers[11] = (0.3701, 0.5418)\n centers[12] = (0.4690, 0.4326)\n centers[13] = (0.6176, 0.5057)\n centers[14] = (0.8956, 0.4985)\n centers[15] = (0.0703, 0.6989)\n centers[16] = (0.2691, 0.6837)\n centers[17] = (0.4867, 0.6269)\n centers[18] = (0.7221, 0.6775)\n centers[19] = (0.9283, 0.7417)\n centers[20] = (0.0953, 0.8687)\n centers[21] = (0.2804, 0.9063)\n centers[22] = (0.5072, 0.8626)\n centers[23] = (0.7281, 0.9113)\n centers[24] = (0.9084, 0.9037)\n centers[25] = (0.3493, 0.1882)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.3039102482480134
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/results/packing_viz.png",
+ "execution_time_mean": 10647.228168863803,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..36110e7c8c4eb41d1047bb0b5f857da2efa6e6dc
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_95/rewrite.txt
@@ -0,0 +1,309 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+# --- Core Utility: Radius Calculation ---
+# Adopted from constructor; more robust with convergence check.
+def compute_max_radii(centers, max_iter=250, convergence_epsilon=1e-9):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This version includes a convergence check for robustness and efficiency.
+ """
+ n = centers.shape[0]
+ # Initial radii are limited by wall distance
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ for i in range(n):
+ for j in range(i + 1, n):
+ vec = centers[i] - centers[j]
+ dist = np.linalg.norm(vec)
+
+ if dist < 1e-9: # If centers are almost co-located
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence after a full pass over all pairs
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+# --- Local Searcher: Force-directed simulation (HybridLocalSearcher) ---
+class HybridLocalSearcher:
+ """
+ A force-directed local search optimizer, encapsulating a two-phase physics simulation.
+ This component refines circle center positions to maximize radii sum in a local region.
+ Combines ideas from multiple high-performing programs for robust optimization.
+ """
+ def __init__(self, sim_iter=300, fine_tune_iter=100, learning_rate=0.02,
+ wall_strength=0.8, initial_growth_pressure=1.05,
+ final_growth_pressure=1.001, fine_tune_lr=0.001,
+ radius_sim_iter=80):
+ self.sim_iter = sim_iter
+ self.fine_tune_iter = fine_tune_iter
+ self.learning_rate = learning_rate
+ self.wall_strength = wall_strength
+ self.initial_growth_pressure = initial_growth_pressure
+ self.final_growth_pressure = final_growth_pressure
+ self.fine_tune_lr = fine_tune_lr
+ self.radius_sim_iter = radius_sim_iter
+
+ def optimize(self, centers):
+ """Refines circle centers using the force-directed simulation."""
+
+ # --- Phase 1: Annealed Growth Pressure ---
+ # This phase uses artificially inflated radii to create strong repulsive forces,
+ # helping circles to escape local minima and find more open spaces.
+ for i_iter in range(self.sim_iter):
+ progress = i_iter / self.sim_iter
+ # Growth pressure anneals from initial to final value
+ growth_pressure = self.final_growth_pressure + \
+ (self.initial_growth_pressure - self.final_growth_pressure) * (1.0 - progress)**2
+
+ # Use faster radius calculation during simulation for performance
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter)
+ pressured_radii = radii * growth_pressure
+
+ forces = self._calculate_forces(centers, pressured_radii)
+
+ # Learning rate also anneals, starting larger for exploration and shrinking for stability
+ lr = self.learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ # --- Phase 2: Fine-Tuning ---
+ # After the aggressive exploration, this phase settles the packing without artificial pressure,
+ # allowing for precise placement based on actual overlaps.
+ for _ in range(self.fine_tune_iter):
+ radii = compute_max_radii(centers, max_iter=self.radius_sim_iter) # Use actual radii for forces
+ forces = self._calculate_forces(centers, radii)
+ centers += forces * self.fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0) # Keep centers within unit square
+
+ return centers
+
+ def _calculate_forces(self, centers, effective_radii):
+ """
+ Calculates repulsion forces from circle overlaps and wall proximity.
+ `effective_radii` can be `pressured_radii` (for phase 1) or `actual radii` (for phase 2).
+ """
+ forces = np.zeros_like(centers)
+
+ # Vectorized Circle-to-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.linalg.norm(diffs, axis=-1)
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero for nearly co-located points
+
+ radii_sums = effective_radii[:, np.newaxis] + effective_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists) # Calculate amount of overlap
+ np.fill_diagonal(overlaps, 0) # Circles don't repel themselves
+
+ force_magnitudes = overlaps # Force magnitude proportional to overlap
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis] # Direction along difference vector
+ forces += np.sum(force_matrix, axis=1) # Sum forces acting on each circle
+
+ # Vectorized Wall repulsion (pushes circles away from boundaries)
+ forces[:, 0] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 0]) # Left wall (x=0)
+ forces[:, 0] -= self.wall_strength * np.maximum(0, (centers[:, 0] + effective_radii) - 1.0) # Right wall (x=1)
+ forces[:, 1] += self.wall_strength * np.maximum(0, effective_radii - centers[:, 1]) # Bottom wall (y=0)
+ forces[:, 1] -= self.wall_strength * np.maximum(0, (centers[:, 1] + effective_radii) - 1.0) # Top wall (y=1)
+
+ return forces
+
+# --- GA Core Components ---
+class Individual:
+ """Represents a single candidate packing solution (a set of circle centers)."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0 # Sum of radii, initialized to a low value
+
+ def evaluate_fitness(self, radius_iter_val):
+ """Calculates the sum of radii for the current center configuration."""
+ self.radii = compute_max_radii(self.centers, max_iter=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """
+ Initializes a diverse population for the Genetic Algorithm.
+ Includes grid-based patterns (strong baselines) and purely random configurations
+ to ensure broad exploration.
+ """
+ population = []
+
+ # 1. Strong baseline from 5x5 grid + one interstitial circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers_base = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ if k < n_circles - 1: # Fill 25 circles for a 26-circle problem
+ grid_centers_base[k, 0] = (i + 0.5) * spacing
+ grid_centers_base[k, 1] = (j + 0.5) * spacing
+ k += 1
+ else:
+ break
+ # Place the 26th circle at a common interstitial point
+ if k < n_circles: # For n=26, this places the 26th circle at index 25
+ grid_centers_base[k] = [spacing, spacing] # e.g., (0.2, 0.2)
+
+ # Add the base grid configuration
+ population.append(Individual(grid_centers_base.copy(), n_circles))
+
+ # Add several perturbed versions of the grid for more diverse starting points
+ for _ in range(population_size // 4): # Approximately 25% of the population
+ perturbed_centers = grid_centers_base + np.random.normal(0, 0.015, (n_circles, 2)) # Increased perturbation strength
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 2. Fill the rest of the population with purely random configurations for broad diversity
+ while len(population) < population_size:
+ random_centers = np.random.rand(n_circles, 2)
+ population.append(Individual(random_centers, n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ # Select parent 1 by picking the fittest from a random tournament subset
+ tournament_p1 = np.random.choice(population, tournament_size, replace=False)
+ parent1 = max(tournament_p1, key=lambda ind: ind.fitness)
+
+ # Select parent 2, ensuring it's different from parent 1
+ # Filter out parent1 from candidates for parent2 to ensure distinctness if possible
+ tournament_p2_candidates = [ind for ind in np.random.choice(population, tournament_size, replace=False) if ind is not parent1]
+ if len(tournament_p2_candidates) > 0:
+ parent2 = max(tournament_p2_candidates, key=lambda ind: ind.fitness)
+ else: # Fallback if parent1 was the only distinct candidate in parent2's tournament subset
+ parent2 = np.random.choice(population)
+ while parent2 is parent1: # Ensure parent2 is definitely different
+ parent2 = np.random.choice(population)
+ return parent1, parent2
+
+def crossover(parent1, parent2, n_circles):
+ """
+ Performs uniform crossover: for each circle, its position (x,y) is inherited
+ from either parent1 or parent2 with 50% probability. Vectorized for efficiency.
+ """
+ child_centers = np.zeros((n_circles, 2))
+ mask = np.random.rand(n_circles, 1) < 0.5 # A mask for each circle, applied to both x and y
+ child_centers = np.where(mask, parent1.centers, parent2.centers)
+ return Individual(np.clip(child_centers, 0.0, 1.0), n_circles) # Clip to keep centers within bounds
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """
+ Applies Gaussian noise mutation to individual circle centers.
+ Each circle has a `mutation_rate` chance to have its (x,y) coordinates
+ perturbed by Gaussian noise with `mutation_strength` standard deviation.
+ """
+ mutated_centers = individual.centers.copy()
+ mask = np.random.rand(n_circles) < mutation_rate # Which circles to mutate
+
+ # Apply Gaussian noise only to selected circles
+ if np.any(mask):
+ mutated_centers[mask] += np.random.normal(0, mutation_strength, (mask.sum(), 2))
+
+ individual.centers = np.clip(mutated_centers, 0.0, 1.0) # Keep within bounds
+ return individual
+
+# --- Main Orchestrator: Memetic Algorithm ---
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm, which hybridizes
+ a Genetic Algorithm with a powerful force-directed local search.
+ This approach combines the exploration power of GA with the refinement capability
+ of local search, incorporating best practices from high-scoring prior programs.
+ """
+ n_circles = 26
+
+ # --- Hyperparameters for Memetic Algorithm ---
+ POPULATION_SIZE = 100 # Number of candidate solutions in each generation
+ NUM_GENERATIONS = 750 # Total number of evolutionary steps
+ MUTATION_RATE = 0.18 # Probability that a single circle's position is mutated
+ INITIAL_MUTATION_STRENGTH = 0.07 # Initial standard deviation for Gaussian mutation noise
+ FINAL_MUTATION_STRENGTH = 0.001 # Final standard deviation (annealed)
+ TOURNAMENT_SIZE = 6 # Number of individuals to compete in tournament selection
+ ELITISM_COUNT = 8 # Number of best individuals to carry over directly
+ LOCAL_SEARCH_PROB = 0.15 # Probability to apply the powerful local search to a new child
+
+ GA_RADIUS_ITER = 100 # Iterations for `compute_max_radii` during GA fitness evaluation (faster)
+ FINAL_RADIUS_ITER = 600 # Iterations for final high-precision evaluation of the best solution
+
+ # --- Initialization ---
+ population = initialize_population(n_circles, POPULATION_SIZE)
+ local_searcher = HybridLocalSearcher() # Instantiate the local search optimizer
+
+ # --- Crucial: Initial Local Optimization of Entire Population (from Prior Program 3) ---
+ # Applying the local search to all individuals in the initial population provides
+ # a strong, diverse set of locally optimized starting points for the GA.
+ # This significantly improves the quality of solutions found early on.
+ for i, individual in enumerate(population):
+ individual.centers = local_searcher.optimize(individual.centers.copy())
+ individual.evaluate_fitness(GA_RADIUS_ITER) # Evaluate fitness after initial local optimization
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(NUM_GENERATIONS):
+ # Anneal mutation strength: decreases over time for fine-tuning
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ # Elitism: Directly carry over the best individuals to the next generation
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ # Generate the rest of the new population through selection, crossover, and mutation
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+
+ # --- Hybrid Operator: Probabilistic Local Search ---
+ # Apply the powerful local search to a subset of children. This is a key
+ # memetic step, allowing the GA to guide global search while local search
+ # refines promising solutions efficiently.
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = local_searcher.optimize(child.centers.copy())
+
+ # Always apply a small standard mutation for ongoing diversity and exploration
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ child.evaluate_fitness(GA_RADIUS_ITER) # Evaluate child's fitness
+ new_population.append(child)
+
+ population = new_population # Replace old population with the new one
+
+ # Update the overall best individual found across all generations
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Optional: print progress to monitor the search
+ # if generation % (NUM_GENERATIONS // 10) == 0:
+ # print(f"Gen {generation+1}/{NUM_GENERATIONS}, Best Fitness: {best_individual_overall.fitness:.4f}, Mutation Strength: {current_mutation_strength:.4f}")
+
+ # Final high-precision evaluation of the best individual found with more iterations
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..5d310f35dcaf361f05f9fdbd50c5fbcfbe8016b5
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/edit.diff
@@ -0,0 +1,414 @@
+--- a/original.py
++++ b/original.py
+@@ -1,197 +1,240 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+-def compute_max_radii(centers):
+- """
+- Compute the maximum possible radii for each circle position
+- such that they don't overlap and stay within the unit square.
+- This uses an iterative proportional scaling method.
++def compute_max_radii(centers, max_iter=1000, convergence_threshold=1e-8):
++ """
++ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+- radii = np.ones(n)
+-
+- for i in range(n):
+- x, y = centers[i]
+- radii[i] = min(x, y, 1 - x, 1 - y)
+-
+- # Iterations for the radii calculation, for robust convergence
+- # This part of the code is intentionally kept as-is, as it's a core utility function
+- # that is not part of the GA's 'evolutionary' algorithm itself, but rather its fitness evaluation.
+- for _ in range(500):
+- updated_in_pass = False
++ # Initialize radii based on distance to walls
++ radii = np.min([
++ centers[:, 0], 1 - centers[:, 0],
++ centers[:, 1], 1 - centers[:, 1]
++ ], axis=0)
++
++ for _ in range(max_iter):
++ old_radii = radii.copy()
++ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+-
+- if dist < 1e-9: # Centers are practically identical
+- # To prevent division by zero and ensure no overlap, assign zero radius
+- radii[i] = 0.0
+- radii[j] = 0.0
+- updated_in_pass = True
+- continue
+-
+ if radii[i] + radii[j] > dist:
+- updated_in_pass = True
+- scale = dist / (radii[i] + radii[j])
+- radii[i] *= scale
+- radii[j] *= scale
+-
+- if not updated_in_pass:
++ if dist < 1e-9: # Avoid division by zero for coincident centers
++ radii[i], radii[j] = 0.0, 0.0
++ else:
++ # Scale radii down proportionally
++ scale = dist / (radii[i] + radii[j])
++ radii[i] *= scale
++ radii[j] *= scale
++ updated = True
++
++ # Convergence check
++ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+-
+- return np.maximum(radii, 0.0) # Ensure no negative radii
++
++ return np.maximum(radii, 0)
++
++
++# This is the powerful local searcher from a previous high-scoring Memetic Algorithm.
++# It will be used as the local minimization step in the Basin Hopping algorithm.
++class HybridLocalSearcher:
++ """A powerful local search refiner based on a two-phase physical simulation."""
++ def __init__(self, n, config):
++ self.n = n
++ self.config = config
++
++ def _compute_radii_for_sim(self, centers):
++ """A fast radius computation for use inside the simulation loop."""
++ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
++
++ def refine(self, centers):
++ """Applies a two-phase physical simulation to refine a configuration."""
++ refined_centers = centers.copy()
++
++ # Phase 1: Main refinement with decreasing growth pressure
++ iterations = self.config['sim_iter']
++ base_lr = self.config['learning_rate']
++ wall_strength = self.config['wall_strength']
++ initial_growth_pressure = self.config['initial_growth_pressure']
++ final_growth_pressure = self.config['final_growth_pressure']
++
++ for i_iter in range(iterations):
++ progress = i_iter / iterations
++ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
++ current_radii = self._compute_radii_for_sim(refined_centers)
++ pressured_radii = current_radii * current_growth_pressure
++
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
++ force_matrix = np.nan_to_num(force_matrix)
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ wall_forces = np.zeros_like(refined_centers)
++ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++ lr = base_lr * (1.0 - progress)**2
++ refined_centers += forces * lr
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ # Phase 2: Fine-tuning with minimal growth pressure
++ iterations_ft = self.config['fine_tune_iter']
++ lr_ft = self.config['fine_tune_lr']
++ for _ in range(iterations_ft):
++ current_radii = self._compute_radii_for_sim(refined_centers)
++
++ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
++ dists = np.sqrt(np.sum(diffs**2, axis=-1))
++ dists[dists < 1e-9] = 1e-9
++ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
++ overlaps = np.maximum(0, radii_sums - dists)
++ np.fill_diagonal(overlaps, 0)
++
++ with np.errstate(divide='ignore', invalid='ignore'):
++ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
++ force_matrix = np.nan_to_num(force_matrix)
++ circle_forces = np.sum(force_matrix, axis=1)
++
++ wall_forces = np.zeros_like(refined_centers)
++ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
++ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
++ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
++ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
++
++ forces = circle_forces + wall_forces
++ refined_centers += forces * lr_ft
++ refined_centers = np.clip(refined_centers, 0.0, 1.0)
++
++ return refined_centers
++
++def _initialize_start_point(n):
++ """Creates a good starting configuration: a perturbed 5x5 grid with one circle in an interstitial space."""
++ centers = np.zeros((n, 2))
++ spacing = 1.0 / 5
++ k = 0
++ for j in range(5):
++ for i in range(5):
++ centers[k, 0] = (i + 0.5) * spacing
++ centers[k, 1] = (j + 0.5) * spacing
++ k += 1
++ # Place 26th circle in a promising spot (e.g., a corner interstitial void)
++ centers[n-1] = [spacing, spacing]
++ # Add a small amount of noise for asymmetry
++ centers += np.random.normal(0, 0.01, size=centers.shape)
++ return np.clip(centers, 0, 1)
++
++def _perturb(centers, num_to_kick, strength):
++ """Applies a 'kick' to the centers to escape a local minimum."""
++ n = centers.shape[0]
++ perturbed_centers = centers.copy()
++
++ indices_to_kick = np.random.choice(n, num_to_kick, replace=False)
++
++ kick_vectors = np.random.normal(0, strength, size=(num_to_kick, 2))
++ perturbed_centers[indices_to_kick] += kick_vectors
++
++ # Add a small probability to completely reset one of the kicked circles to a random position.
++ # This acts as a diversification mechanism to prevent getting stuck in a large meta-basin.
++ if np.random.rand() < 0.25:
++ idx_to_reset = np.random.choice(indices_to_kick)
++ perturbed_centers[idx_to_reset] = np.random.rand(2)
++
++ return np.clip(perturbed_centers, 0, 1)
+
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles using a Genetic Algorithm.
+-
+- Returns:
+- Tuple of (centers, radii)
+- centers: np.array of shape (26, 2) with (x, y) coordinates
+- radii: np.array of shape (26) with radius of each circle
++ Constructs a packing of 26 circles using a Basin Hopping metaheuristic.
+ """
+ n = 26
+-
+- # --- Genetic Algorithm Parameters ---
+- POPULATION_SIZE = 100
+- GENERATIONS = 500
+- MUTATION_RATE = 0.2 # Probability of a circle's coordinates being mutated
+- CROSSOVER_RATE = 0.8 # Probability of two parents undergoing crossover
+- MUTATION_STRENGTH_START = 0.1 # Initial std dev for Gaussian mutation
+- MUTATION_STRENGTH_END = 0.005 # Final std dev for Gaussian mutation (annealed)
+- ELITE_COUNT = 5 # Number of best individuals to carry over directly
+- TOURNAMENT_SIZE = 5 # Number of individuals in a tournament for parent selection
+-
+- # --- Initialization ---
+- population = []
+- for i in range(POPULATION_SIZE):
+- if i < POPULATION_SIZE // 4: # Some individuals start from a perturbed grid
+- # 5x5 grid for 25 circles
+- num_cells_side = 5
+- spacing = 1.0 / num_cells_side
+- initial_centers = np.zeros((n, 2))
+- k = 0
+- for row in range(num_cells_side):
+- for col in range(num_cells_side):
+- initial_centers[k, 0] = (col + 0.5) * spacing
+- initial_centers[k, 1] = (row + 0.5) * spacing
+- k += 1
+- # Perturb the grid slightly
+- initial_centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+- initial_centers[:25, :] = np.clip(initial_centers[:25, :], 0.0, 1.0)
+- # Place the 26th circle at an interstitial point for initial diversity, with small perturbation
+- if n > 25:
+- initial_centers[25] = [spacing, spacing] + np.random.normal(0, spacing * 0.02, size=2)
+- initial_centers[25] = np.clip(initial_centers[25], 0.0, 1.0)
+- population.append(initial_centers)
+- else: # Most individuals start completely randomly for broader exploration
+- population.append(np.random.rand(n, 2))
+-
+-
+- best_individual_centers = None
+- best_overall_fitness = -1.0
+-
+- # --- Main Genetic Algorithm Loop ---
+- for generation in range(GENERATIONS):
+- # Calculate fitness for each individual
+- fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in population])
+-
+- # Track the best individual in the current population
+- current_best_idx = np.argmax(fitnesses)
+- current_best_fitness = fitnesses[current_best_idx]
+-
+- if current_best_fitness > best_overall_fitness:
+- best_overall_fitness = current_best_fitness
+- best_individual_centers = population[current_best_idx].copy()
+-
+- # Create the next generation
+- new_population = []
+-
+- # Elitism: Carry over the best individuals directly
+- elite_indices = np.argsort(fitnesses)[-ELITE_COUNT:]
+- for idx in elite_indices:
+- new_population.append(population[idx].copy())
+-
+- # Anneal mutation strength for exploration-exploitation trade-off
+- # Cubic decay ensures faster reduction of mutation strength towards fine-tuning
+- mutation_strength = MUTATION_STRENGTH_END + \
+- (MUTATION_STRENGTH_START - MUTATION_STRENGTH_END) * \
+- (1.0 - (generation / GENERATIONS))**1.5
+-
+- # Fill the rest of the new population
+- while len(new_population) < POPULATION_SIZE:
+- # Selection: Choose two parents using tournament selection
+- parent1 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+- parent2 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+-
+- # Crossover: Combine genetic material from parents
+- if np.random.rand() < CROSSOVER_RATE:
+- child = arithmetic_crossover(parent1, parent2)
+- else: # If no crossover, one parent (e.g., parent1) becomes the child
+- child = parent1.copy()
+-
+- # Mutation: Introduce random changes
+- child = gaussian_mutate(child, MUTATION_RATE, mutation_strength)
+-
+- new_population.append(child)
+-
+- population = new_population
+-
+- # After all generations, return the best found individual
+- final_centers = best_individual_centers
+- final_radii = compute_max_radii(final_centers)
+-
+- return final_centers, final_radii
+-
+-
+-def select_parent(population, fitnesses, tournament_size):
+- """
+- Selects a parent using tournament selection.
+- """
+- tournament_indices = np.random.choice(len(population), size=tournament_size, replace=False)
+- tournament_fitnesses = fitnesses[tournament_indices]
+- winner_index_in_tournament = np.argmax(tournament_fitnesses)
+- winner_original_index = tournament_indices[winner_index_in_tournament]
+- return population[winner_original_index]
+-
+-def arithmetic_crossover(parent1, parent2):
+- """
+- Performs arithmetic crossover for continuous variables.
+- Child = alpha * Parent1 + (1-alpha) * Parent2
+- """
+- alpha = np.random.uniform(0.3, 0.7) # Blend factor for averaging parents
+- child = alpha * parent1 + (1 - alpha) * parent2
+- return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+-
+-def gaussian_mutate(individual, mutation_rate, mutation_strength):
+- """
+- Applies Gaussian mutation to an individual's circle centers.
+- Each circle's coordinates have a `mutation_rate` chance of being perturbed.
+- """
+- mutated_individual = individual.copy()
+- num_circles = individual.shape[0]
+-
+- # Iterate through each circle and apply mutation based on mutation_rate
+- for i in range(num_circles):
+- if np.random.rand() < mutation_rate:
+- mutated_individual[i, 0] += np.random.normal(0, mutation_strength)
+- mutated_individual[i, 1] += np.random.normal(0, mutation_strength)
+-
+- # Introduce a small chance for a "strong" mutation (random reset)
+- # This helps escape local optima by completely changing a circle's position
+- if np.random.rand() < 0.01: # 1% chance for a strong mutation
+- idx_to_reset = np.random.randint(0, num_circles)
+- mutated_individual[idx_to_reset, :] = np.random.rand(2) # New random position
+-
+- return np.clip(mutated_individual, 0.0, 1.0)
++
++ # --- Basin Hopping Parameters ---
++ BH_ITERATIONS = 200 # Number of hopping steps. Each step is computationally expensive.
++ KICK_STRENGTH = 0.12 # Standard deviation for Gaussian perturbation.
++ KICK_NUM_CIRCLES = 4 # Number of circles to perturb in each kick.
++ TEMPERATURE = 0.008 # Metropolis criterion temperature. Controls acceptance of worse solutions.
++
++ # --- Local Searcher Configuration ---
++ # These parameters are adopted from a previous high-scoring Memetic Algorithm.
++ ls_config = {
++ 'sim_iter': 400,
++ 'radius_sim_iter': 100,
++ 'learning_rate': 0.015,
++ 'wall_strength': 0.6,
++ 'initial_growth_pressure': 1.04,
++ 'final_growth_pressure': 1.001,
++ 'fine_tune_iter': 200,
++ 'fine_tune_lr': 0.0015,
++ }
++ minimizer = HybridLocalSearcher(n, ls_config)
++
++ # 1. Initialization
++ current_centers = _initialize_start_point(n)
++
++ # Perform an initial minimization to find the first local optimum.
++ current_centers = minimizer.refine(current_centers)
++ current_radii = compute_max_radii(current_centers)
++ # The "energy" is the negative of the sum of radii, as we want to maximize it.
++ current_energy = -np.sum(current_radii)
++
++ best_centers = current_centers.copy()
++ best_energy = current_energy
++
++ # 2. Main Basin Hopping Loop
++ for _ in range(BH_ITERATIONS):
++ # a. Perturbation ("Kick"): Escape the current basin.
++ kicked_centers = _perturb(current_centers, KICK_NUM_CIRCLES, KICK_STRENGTH)
++
++ # b. Local Minimization: Find the bottom of the new basin.
++ new_centers = minimizer.refine(kicked_centers)
++
++ # c. Calculate New Energy
++ new_radii = compute_max_radii(new_centers)
++ new_energy = -np.sum(new_radii)
++
++ # d. Metropolis Acceptance Criterion
++ accept = False
++ if new_energy < current_energy:
++ accept = True
++ else:
++ delta_e = new_energy - current_energy
++ # Accept a worse solution with a certain probability to cross energy barriers.
++ if TEMPERATURE > 1e-9: # Avoid division by zero
++ accept_prob = np.exp(-delta_e / TEMPERATURE)
++ if np.random.rand() < accept_prob:
++ accept = True
++
++ if accept:
++ current_centers = new_centers
++ current_energy = new_energy
++
++ # e. Update Global Best Solution
++ # This is done regardless of the acceptance step, ensuring we never lose the best solution found.
++ if new_energy < best_energy:
++ best_energy = new_energy
++ best_centers = new_centers.copy()
++
++ # After all iterations, return the best configuration found.
++ final_radii = compute_max_radii(best_centers)
++ return best_centers, final_radii
++
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..82d0d5af12e501f3dcdc87917a6133f233413840
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/main.py
@@ -0,0 +1,240 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=1000, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Avoid division by zero for coincident centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ # Scale radii down proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Convergence check
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# This is the powerful local searcher from a previous high-scoring Memetic Algorithm.
+# It will be used as the local minimization step in the Basin Hopping algorithm.
+class HybridLocalSearcher:
+ """A powerful local search refiner based on a two-phase physical simulation."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ force_matrix = np.nan_to_num(force_matrix)
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ force_matrix = np.nan_to_num(force_matrix)
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+def _initialize_start_point(n):
+ """Creates a good starting configuration: a perturbed 5x5 grid with one circle in an interstitial space."""
+ centers = np.zeros((n, 2))
+ spacing = 1.0 / 5
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place 26th circle in a promising spot (e.g., a corner interstitial void)
+ centers[n-1] = [spacing, spacing]
+ # Add a small amount of noise for asymmetry
+ centers += np.random.normal(0, 0.01, size=centers.shape)
+ return np.clip(centers, 0, 1)
+
+def _perturb(centers, num_to_kick, strength):
+ """Applies a 'kick' to the centers to escape a local minimum."""
+ n = centers.shape[0]
+ perturbed_centers = centers.copy()
+
+ indices_to_kick = np.random.choice(n, num_to_kick, replace=False)
+
+ kick_vectors = np.random.normal(0, strength, size=(num_to_kick, 2))
+ perturbed_centers[indices_to_kick] += kick_vectors
+
+ # Add a small probability to completely reset one of the kicked circles to a random position.
+ # This acts as a diversification mechanism to prevent getting stuck in a large meta-basin.
+ if np.random.rand() < 0.25:
+ idx_to_reset = np.random.choice(indices_to_kick)
+ perturbed_centers[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(perturbed_centers, 0, 1)
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Basin Hopping metaheuristic.
+ """
+ n = 26
+
+ # --- Basin Hopping Parameters ---
+ BH_ITERATIONS = 200 # Number of hopping steps. Each step is computationally expensive.
+ KICK_STRENGTH = 0.12 # Standard deviation for Gaussian perturbation.
+ KICK_NUM_CIRCLES = 4 # Number of circles to perturb in each kick.
+ TEMPERATURE = 0.008 # Metropolis criterion temperature. Controls acceptance of worse solutions.
+
+ # --- Local Searcher Configuration ---
+ # These parameters are adopted from a previous high-scoring Memetic Algorithm.
+ ls_config = {
+ 'sim_iter': 400,
+ 'radius_sim_iter': 100,
+ 'learning_rate': 0.015,
+ 'wall_strength': 0.6,
+ 'initial_growth_pressure': 1.04,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 200,
+ 'fine_tune_lr': 0.0015,
+ }
+ minimizer = HybridLocalSearcher(n, ls_config)
+
+ # 1. Initialization
+ current_centers = _initialize_start_point(n)
+
+ # Perform an initial minimization to find the first local optimum.
+ current_centers = minimizer.refine(current_centers)
+ current_radii = compute_max_radii(current_centers)
+ # The "energy" is the negative of the sum of radii, as we want to maximize it.
+ current_energy = -np.sum(current_radii)
+
+ best_centers = current_centers.copy()
+ best_energy = current_energy
+
+ # 2. Main Basin Hopping Loop
+ for _ in range(BH_ITERATIONS):
+ # a. Perturbation ("Kick"): Escape the current basin.
+ kicked_centers = _perturb(current_centers, KICK_NUM_CIRCLES, KICK_STRENGTH)
+
+ # b. Local Minimization: Find the bottom of the new basin.
+ new_centers = minimizer.refine(kicked_centers)
+
+ # c. Calculate New Energy
+ new_radii = compute_max_radii(new_centers)
+ new_energy = -np.sum(new_radii)
+
+ # d. Metropolis Acceptance Criterion
+ accept = False
+ if new_energy < current_energy:
+ accept = True
+ else:
+ delta_e = new_energy - current_energy
+ # Accept a worse solution with a certain probability to cross energy barriers.
+ if TEMPERATURE > 1e-9: # Avoid division by zero
+ accept_prob = np.exp(-delta_e / TEMPERATURE)
+ if np.random.rand() < accept_prob:
+ accept = True
+
+ if accept:
+ current_centers = new_centers
+ current_energy = new_energy
+
+ # e. Update Global Best Solution
+ # This is done regardless of the acceptance step, ensuring we never lose the best solution found.
+ if new_energy < best_energy:
+ best_energy = new_energy
+ best_centers = new_centers.copy()
+
+ # After all iterations, return the best configuration found.
+ final_radii = compute_max_radii(best_centers)
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..30f34f3a8f19e8ccec64b3ac3a0830432b7c4aca
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/original.py
@@ -0,0 +1,197 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method.
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ for i in range(n):
+ x, y = centers[i]
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Iterations for the radii calculation, for robust convergence
+ # This part of the code is intentionally kept as-is, as it's a core utility function
+ # that is not part of the GA's 'evolutionary' algorithm itself, but rather its fitness evaluation.
+ for _ in range(500):
+ updated_in_pass = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Centers are practically identical
+ # To prevent division by zero and ensure no overlap, assign zero radius
+ radii[i] = 0.0
+ radii[j] = 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ updated_in_pass = True
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ if not updated_in_pass:
+ break
+
+ return np.maximum(radii, 0.0) # Ensure no negative radii
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Genetic Algorithm.
+
+ Returns:
+ Tuple of (centers, radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ """
+ n = 26
+
+ # --- Genetic Algorithm Parameters ---
+ POPULATION_SIZE = 100
+ GENERATIONS = 500
+ MUTATION_RATE = 0.2 # Probability of a circle's coordinates being mutated
+ CROSSOVER_RATE = 0.8 # Probability of two parents undergoing crossover
+ MUTATION_STRENGTH_START = 0.1 # Initial std dev for Gaussian mutation
+ MUTATION_STRENGTH_END = 0.005 # Final std dev for Gaussian mutation (annealed)
+ ELITE_COUNT = 5 # Number of best individuals to carry over directly
+ TOURNAMENT_SIZE = 5 # Number of individuals in a tournament for parent selection
+
+ # --- Initialization ---
+ population = []
+ for i in range(POPULATION_SIZE):
+ if i < POPULATION_SIZE // 4: # Some individuals start from a perturbed grid
+ # 5x5 grid for 25 circles
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ initial_centers = np.zeros((n, 2))
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ # Perturb the grid slightly
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
+ initial_centers[:25, :] = np.clip(initial_centers[:25, :], 0.0, 1.0)
+ # Place the 26th circle at an interstitial point for initial diversity, with small perturbation
+ if n > 25:
+ initial_centers[25] = [spacing, spacing] + np.random.normal(0, spacing * 0.02, size=2)
+ initial_centers[25] = np.clip(initial_centers[25], 0.0, 1.0)
+ population.append(initial_centers)
+ else: # Most individuals start completely randomly for broader exploration
+ population.append(np.random.rand(n, 2))
+
+
+ best_individual_centers = None
+ best_overall_fitness = -1.0
+
+ # --- Main Genetic Algorithm Loop ---
+ for generation in range(GENERATIONS):
+ # Calculate fitness for each individual
+ fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in population])
+
+ # Track the best individual in the current population
+ current_best_idx = np.argmax(fitnesses)
+ current_best_fitness = fitnesses[current_best_idx]
+
+ if current_best_fitness > best_overall_fitness:
+ best_overall_fitness = current_best_fitness
+ best_individual_centers = population[current_best_idx].copy()
+
+ # Create the next generation
+ new_population = []
+
+ # Elitism: Carry over the best individuals directly
+ elite_indices = np.argsort(fitnesses)[-ELITE_COUNT:]
+ for idx in elite_indices:
+ new_population.append(population[idx].copy())
+
+ # Anneal mutation strength for exploration-exploitation trade-off
+ # Cubic decay ensures faster reduction of mutation strength towards fine-tuning
+ mutation_strength = MUTATION_STRENGTH_END + \
+ (MUTATION_STRENGTH_START - MUTATION_STRENGTH_END) * \
+ (1.0 - (generation / GENERATIONS))**1.5
+
+ # Fill the rest of the new population
+ while len(new_population) < POPULATION_SIZE:
+ # Selection: Choose two parents using tournament selection
+ parent1 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+ parent2 = select_parent(population, fitnesses, TOURNAMENT_SIZE)
+
+ # Crossover: Combine genetic material from parents
+ if np.random.rand() < CROSSOVER_RATE:
+ child = arithmetic_crossover(parent1, parent2)
+ else: # If no crossover, one parent (e.g., parent1) becomes the child
+ child = parent1.copy()
+
+ # Mutation: Introduce random changes
+ child = gaussian_mutate(child, MUTATION_RATE, mutation_strength)
+
+ new_population.append(child)
+
+ population = new_population
+
+ # After all generations, return the best found individual
+ final_centers = best_individual_centers
+ final_radii = compute_max_radii(final_centers)
+
+ return final_centers, final_radii
+
+
+def select_parent(population, fitnesses, tournament_size):
+ """
+ Selects a parent using tournament selection.
+ """
+ tournament_indices = np.random.choice(len(population), size=tournament_size, replace=False)
+ tournament_fitnesses = fitnesses[tournament_indices]
+ winner_index_in_tournament = np.argmax(tournament_fitnesses)
+ winner_original_index = tournament_indices[winner_index_in_tournament]
+ return population[winner_original_index]
+
+def arithmetic_crossover(parent1, parent2):
+ """
+ Performs arithmetic crossover for continuous variables.
+ Child = alpha * Parent1 + (1-alpha) * Parent2
+ """
+ alpha = np.random.uniform(0.3, 0.7) # Blend factor for averaging parents
+ child = alpha * parent1 + (1 - alpha) * parent2
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
+
+def gaussian_mutate(individual, mutation_rate, mutation_strength):
+ """
+ Applies Gaussian mutation to an individual's circle centers.
+ Each circle's coordinates have a `mutation_rate` chance of being perturbed.
+ """
+ mutated_individual = individual.copy()
+ num_circles = individual.shape[0]
+
+ # Iterate through each circle and apply mutation based on mutation_rate
+ for i in range(num_circles):
+ if np.random.rand() < mutation_rate:
+ mutated_individual[i, 0] += np.random.normal(0, mutation_strength)
+ mutated_individual[i, 1] += np.random.normal(0, mutation_strength)
+
+ # Introduce a small chance for a "strong" mutation (random reset)
+ # This helps escape local optima by completely changing a circle's position
+ if np.random.rand() < 0.01: # 1% chance for a strong mutation
+ idx_to_reset = np.random.randint(0, num_circles)
+ mutated_individual[idx_to_reset, :] = np.random.rand(2) # New random position
+
+ return np.clip(mutated_individual, 0.0, 1.0)
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5224a2210e92b2432efc3e2f305800bf022903f4
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_96/rewrite.txt
@@ -0,0 +1,231 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=1000, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Avoid division by zero for coincident centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ # Scale radii down proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Convergence check
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+# This is the powerful local searcher from a previous high-scoring Memetic Algorithm.
+# It will be used as the local minimization step in the Basin Hopping algorithm.
+class HybridLocalSearcher:
+ """A powerful local search refiner based on a two-phase physical simulation."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ force_matrix = np.nan_to_num(force_matrix)
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ with np.errstate(divide='ignore', invalid='ignore'):
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ force_matrix = np.nan_to_num(force_matrix)
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+def _initialize_start_point(n):
+ """Creates a good starting configuration: a perturbed 5x5 grid with one circle in an interstitial space."""
+ centers = np.zeros((n, 2))
+ spacing = 1.0 / 5
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ centers[k, 0] = (i + 0.5) * spacing
+ centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ # Place 26th circle in a promising spot (e.g., a corner interstitial void)
+ centers[n-1] = [spacing, spacing]
+ # Add a small amount of noise for asymmetry
+ centers += np.random.normal(0, 0.01, size=centers.shape)
+ return np.clip(centers, 0, 1)
+
+def _perturb(centers, num_to_kick, strength):
+ """Applies a 'kick' to the centers to escape a local minimum."""
+ n = centers.shape[0]
+ perturbed_centers = centers.copy()
+
+ indices_to_kick = np.random.choice(n, num_to_kick, replace=False)
+
+ kick_vectors = np.random.normal(0, strength, size=(num_to_kick, 2))
+ perturbed_centers[indices_to_kick] += kick_vectors
+
+ # Add a small probability to completely reset one of the kicked circles to a random position.
+ # This acts as a diversification mechanism to prevent getting stuck in a large meta-basin.
+ if np.random.rand() < 0.25:
+ idx_to_reset = np.random.choice(indices_to_kick)
+ perturbed_centers[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(perturbed_centers, 0, 1)
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Basin Hopping metaheuristic.
+ """
+ n = 26
+
+ # --- Basin Hopping Parameters ---
+ BH_ITERATIONS = 200 # Number of hopping steps. Each step is computationally expensive.
+ KICK_STRENGTH = 0.12 # Standard deviation for Gaussian perturbation.
+ KICK_NUM_CIRCLES = 4 # Number of circles to perturb in each kick.
+ TEMPERATURE = 0.008 # Metropolis criterion temperature. Controls acceptance of worse solutions.
+
+ # --- Local Searcher Configuration ---
+ # These parameters are adopted from a previous high-scoring Memetic Algorithm.
+ ls_config = {
+ 'sim_iter': 400,
+ 'radius_sim_iter': 100,
+ 'learning_rate': 0.015,
+ 'wall_strength': 0.6,
+ 'initial_growth_pressure': 1.04,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 200,
+ 'fine_tune_lr': 0.0015,
+ }
+ minimizer = HybridLocalSearcher(n, ls_config)
+
+ # 1. Initialization
+ current_centers = _initialize_start_point(n)
+
+ # Perform an initial minimization to find the first local optimum.
+ current_centers = minimizer.refine(current_centers)
+ current_radii = compute_max_radii(current_centers)
+ # The "energy" is the negative of the sum of radii, as we want to maximize it.
+ current_energy = -np.sum(current_radii)
+
+ best_centers = current_centers.copy()
+ best_energy = current_energy
+
+ # 2. Main Basin Hopping Loop
+ for _ in range(BH_ITERATIONS):
+ # a. Perturbation ("Kick"): Escape the current basin.
+ kicked_centers = _perturb(current_centers, KICK_NUM_CIRCLES, KICK_STRENGTH)
+
+ # b. Local Minimization: Find the bottom of the new basin.
+ new_centers = minimizer.refine(kicked_centers)
+
+ # c. Calculate New Energy
+ new_radii = compute_max_radii(new_centers)
+ new_energy = -np.sum(new_radii)
+
+ # d. Metropolis Acceptance Criterion
+ accept = False
+ if new_energy < current_energy:
+ accept = True
+ else:
+ delta_e = new_energy - current_energy
+ # Accept a worse solution with a certain probability to cross energy barriers.
+ if TEMPERATURE > 1e-9: # Avoid division by zero
+ accept_prob = np.exp(-delta_e / TEMPERATURE)
+ if np.random.rand() < accept_prob:
+ accept = True
+
+ if accept:
+ current_centers = new_centers
+ current_energy = new_energy
+
+ # e. Update Global Best Solution
+ # This is done regardless of the acceptance step, ensuring we never lose the best solution found.
+ if new_energy < best_energy:
+ best_energy = new_energy
+ best_centers = new_centers.copy()
+
+ # After all iterations, return the best configuration found.
+ final_radii = compute_max_radii(best_centers)
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..c06eca71b29de8a348f718882b9bf7748be6c43a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/edit.diff
@@ -0,0 +1,440 @@
+--- a/original.py
++++ b/original.py
+@@ -1,275 +1,216 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+-
+-def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+- """
+- Compute maximum radii using iterative proportional scaling with a convergence check.
++import math
++import random
++
++def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
++ """
++ Compute maximum radii using iterative proportional scaling.
++ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
++ if n == 0:
++ return np.array([])
++
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+- dist = np.linalg.norm(centers[i] - centers[j])
++ dist_sq = np.sum((centers[i] - centers[j])**2)
++ dist = np.sqrt(dist_sq)
++
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+-class HybridLocalSearcher:
+- """
+- A powerful local search refiner based on a two-phase physical simulation.
+- This replaces the simpler force-directed approach.
+- """
+- def __init__(self, n, config):
++class SimulatedAnnealer:
++ """
++ Performs a search for an optimal circle packing using Simulated Annealing.
++ """
++ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+-
+- def _compute_radii_for_sim(self, centers):
+- """A fast radius computation for use inside the simulation loop."""
+- return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+-
+- def refine(self, centers):
+- """Applies a two-phase physical simulation to refine a configuration."""
+- refined_centers = centers.copy()
+-
+- # Phase 1: Main refinement with decreasing growth pressure
+- iterations = self.config['sim_iter']
+- base_lr = self.config['learning_rate']
+- wall_strength = self.config['wall_strength']
+- initial_growth_pressure = self.config['initial_growth_pressure']
+- final_growth_pressure = self.config['final_growth_pressure']
+-
+- for i_iter in range(iterations):
+- progress = i_iter / iterations
+- current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+- current_radii = self._compute_radii_for_sim(refined_centers)
+- pressured_radii = current_radii * current_growth_pressure
+-
+- # Vectorized force calculations
+- diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9
+- radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+- overlaps = np.maximum(0, radii_sums - dists)
+- np.fill_diagonal(overlaps, 0)
+- force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- wall_forces = np.zeros_like(refined_centers)
+- wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+- wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+-
+- forces = circle_forces + wall_forces
+- lr = base_lr * (1.0 - progress)**2
+- refined_centers += forces * lr
+- refined_centers = np.clip(refined_centers, 0.0, 1.0)
+-
+- # Phase 2: Fine-tuning with minimal growth pressure
+- iterations_ft = self.config['fine_tune_iter']
+- lr_ft = self.config['fine_tune_lr']
+- for _ in range(iterations_ft):
+- current_radii = self._compute_radii_for_sim(refined_centers)
+-
+- diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+- dists = np.sqrt(np.sum(diffs**2, axis=-1))
+- dists[dists < 1e-9] = 1e-9
+- radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+- overlaps = np.maximum(0, radii_sums - dists)
+- np.fill_diagonal(overlaps, 0)
+- force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+- circle_forces = np.sum(force_matrix, axis=1)
+-
+- wall_forces = np.zeros_like(refined_centers)
+- wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+- wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+- wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+- wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+-
+- forces = circle_forces + wall_forces
+- refined_centers += forces * lr_ft
+- refined_centers = np.clip(refined_centers, 0.0, 1.0)
+-
+- return refined_centers
+-
+-
+-class MemeticAlgorithm:
+- """Encapsulates the entire Memetic Algorithm for circle packing."""
+- def __init__(self, n, config):
+- self.n = n
+- self.config = config
+- self.population = []
+- self.fitnesses = np.array([])
+- self.best_solution = None
+- self.best_fitness = -1.0
+- self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+-
+- def _initialize_population(self):
+- """Initializes a diverse population using strategic grid-based starts and random individuals."""
+- spacing = 1.0 / 5
+- candidate_extra_positions = [
+- [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+- [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+- [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+- [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+- ]
+-
+- base_centers = np.zeros((self.n - 1, 2))
+- k = 0
+- for j in range(5):
+- for i in range(5):
+- base_centers[k, 0] = (i + 0.5) * spacing
+- base_centers[k, 1] = (j + 0.5) * spacing
+- k += 1
+-
+- num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+-
+- for i in range(num_strategic_starts):
+- centers = np.zeros((self.n, 2))
+- perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+- perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+- centers[:self.n-1] = perturbed_base_centers
+- centers[self.n-1] = np.array(candidate_extra_positions[i])
+- self.population.append(centers)
+-
+- num_random_starts = self.config['population_size'] - len(self.population)
+- for _ in range(num_random_starts):
+- self.population.append(np.random.rand(self.n, 2))
+-
+- def _evaluate_population(self):
+- """Calculates fitness for the population and updates the best solution."""
+- self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+- best_idx = np.argmax(self.fitnesses)
+- if self.fitnesses[best_idx] > self.best_fitness:
+- self.best_fitness = self.fitnesses[best_idx]
+- self.best_solution = self.population[best_idx].copy()
+-
+- def _select_parent(self):
+- """Selects a parent using tournament selection."""
+- tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+- tourn_fitnesses = self.fitnesses[tourn_indices]
+- winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+- return self.population[winner_idx]
+-
+- def _crossover(self, p1, p2):
+- """Performs uniform crossover, taking whole circles from either parent."""
+- child = p1.copy()
+- mask = np.random.rand(self.n) < 0.5
+- child[mask] = p2[mask]
+- return child
+-
+- def _mutate(self, individual, strength):
+- """Applies Gaussian mutation and a chance of a strong mutation."""
+- mutated_ind = individual.copy()
+- for i in range(self.n):
+- if np.random.rand() < self.config['mutation_rate']:
+- noise = np.random.normal(0, strength, size=2)
+- mutated_ind[i] += noise
+-
+- if np.random.rand() < 0.02:
+- idx_to_reset = np.random.randint(0, self.n)
+- mutated_ind[idx_to_reset] = np.random.rand(2)
+-
+- return np.clip(mutated_ind, 0.0, 1.0)
++ self.centers = initial_centers
++
++ self.temp = config['t_start']
++ self.max_step_size = config['max_step_size']
++
++ self.energy = self._calculate_energy(self.centers)
++ self.best_centers = self.centers.copy()
++ self.best_energy = self.energy
++
++ self.move_weights = config['move_weights']
++ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
++
++ def _calculate_energy(self, centers):
++ """Energy is the negative sum of radii, to be minimized."""
++ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
++ return -np.sum(radii)
++
++ def _propose_move(self):
++ """
++ Proposes a new state by selecting a move type and applying it.
++ The step size is annealed with the temperature.
++ """
++ # Choose a move type based on weights
++ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
++
++ # Anneal step size
++ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
++ current_step_size = self.max_step_size * t_progress
++
++ return move_func(current_step_size)
++
++ def _move_single_circle(self, step_size):
++ """Move a single randomly chosen circle."""
++ new_centers = self.centers.copy()
++ idx = random.randint(0, self.n - 1)
++
++ displacement = np.random.normal(0, step_size, size=2)
++ new_centers[idx] += displacement
++ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
++ return new_centers
++
++ def _move_cluster(self, step_size):
++ """Move a small cluster of neighboring circles."""
++ new_centers = self.centers.copy()
++ cluster_size = self.config['cluster_size']
++
++ # Pick a seed circle
++ seed_idx = random.randint(0, self.n - 1)
++
++ # Find its neighbors
++ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
++ neighbor_indices = np.argsort(dists)[:cluster_size]
++
++ # Apply a common displacement
++ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
++ new_centers[neighbor_indices] += displacement
++ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
++ return new_centers
++
++ def _swap_circles(self, step_size):
++ """Swap the positions of two randomly chosen circles."""
++ new_centers = self.centers.copy()
++ if self.n < 2: return new_centers
++
++ idx1, idx2 = random.sample(range(self.n), 2)
++ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
++ return new_centers
+
+ def run(self):
+- """Executes the full memetic algorithm evolution."""
+- self._initialize_population()
+-
+- for gen in range(self.config['generations']):
+- self._evaluate_population()
+-
+- new_population = []
+-
+- elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+- for idx in elite_indices:
+- new_population.append(self.population[idx].copy())
+-
+- mut_strength = self.config['mut_strength_end'] + \
+- (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+- (1.0 - (gen / self.config['generations']))**2.0
+-
+- while len(new_population) < self.config['population_size']:
+- p1 = self._select_parent()
+- p2 = self._select_parent()
+-
+- child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+- child = self._mutate(child, mut_strength)
+-
+- if np.random.rand() < self.config['local_search_prob']:
+- child = self.local_search_refiner.refine(child)
+-
+- new_population.append(child)
+-
+- self.population = new_population
+-
+- self._evaluate_population()
+- return self.best_solution
++ """Executes the simulated annealing search."""
++ while self.temp > self.config['t_end']:
++ for _ in range(self.config['moves_per_temp']):
++ # Propose a new configuration
++ new_centers = self._propose_move()
++
++ # Calculate its energy
++ new_energy = self._calculate_energy(new_centers)
++
++ delta_e = new_energy - self.energy
++
++ # Acceptance criterion
++ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
++ self.centers = new_centers
++ self.energy = new_energy
++
++ # Update best-ever solution
++ if self.energy < self.best_energy:
++ self.best_energy = self.energy
++ self.best_centers = self.centers.copy()
++
++ # Cool down the temperature
++ self.temp *= self.config['cooling_rate']
++
++ return self.best_centers
+
+
+ def construct_packing():
+ """
+- Constructs a packing of 26 circles using a hybrid Memetic Algorithm.
++ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+- # MA parameters
+- 'population_size': 50,
+- 'generations': 250,
+- 'elite_count': 3,
+- 'tournament_size': 4,
+- 'mutation_rate': 0.35,
+- 'mut_strength_start': 0.1,
+- 'mut_strength_end': 0.001,
+- 'crossover_rate': 0.9,
+- 'initial_perturbation_scale': 0.01,
+-
+- # Memetic Parameters
+- 'local_search_prob': 0.1,
+- 'ls_config': {
+- 'sim_iter': 400,
+- 'radius_sim_iter': 100,
+- 'learning_rate': 0.015,
+- 'wall_strength': 0.6,
+- 'initial_growth_pressure': 1.04,
+- 'final_growth_pressure': 1.001,
+- 'fine_tune_iter': 200,
+- 'fine_tune_lr': 0.0015,
+- }
++ 'num_starts': 12, # Number of independent SA runs
++ 't_start': 0.05, # Initial temperature
++ 't_end': 1e-6, # Final temperature
++ 'cooling_rate': 0.998, # Geometric cooling factor
++ 'moves_per_temp': 60, # Number of moves at each temperature step
++ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
++ 'radius_iter': 250, # Radius calculation iterations during search
++ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
++ 'cluster_size': 4,
+ }
+
+- solver = MemeticAlgorithm(n=n, config=config)
+- best_centers = solver.run()
++ best_overall_centers = None
++ best_overall_score = -1.0
++
++ # Multi-start loop
++ for i in range(config['num_starts']):
++ # --- Generate diverse initial configurations ---
++ initial_centers = np.zeros((n, 2))
++ if i < config['num_starts'] // 2:
++ # Start with a perturbed 5x5 grid + 1 extra circle
++ num_cells_side = 5
++ spacing = 1.0 / num_cells_side
++ k = 0
++ for row in range(num_cells_side):
++ for col in range(num_cells_side):
++ initial_centers[k, 0] = (col + 0.5) * spacing
++ initial_centers[k, 1] = (row + 0.5) * spacing
++ k += 1
++ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
++
++ # Place the 26th circle in a strategic corner or center
++ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
++ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
++ initial_centers = np.clip(initial_centers, 0.0, 1.0)
++ else:
++ # Start with a purely random configuration
++ initial_centers = np.random.rand(n, 2)
++
++ # Run the annealer for this start
++ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
++ result_centers = solver.run()
++
++ # Evaluate and update the best overall result
++ radii = compute_max_radii(result_centers, max_iter=2000)
++ score = np.sum(radii)
++
++ if score > best_overall_score:
++ best_overall_score = score
++ best_overall_centers = result_centers
++
++ # Final high-precision radius calculation for the best solution found
++ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+- final_radii = compute_max_radii(best_centers, max_iter=2000)
+-
+- return best_centers, final_radii
++ return best_overall_centers, final_radii
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..af8c878afd2378eb48f3b44089ae4504fcfca048
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/main.py
@@ -0,0 +1,216 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 12, # Number of independent SA runs
+ 't_start': 0.05, # Initial temperature
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.998, # Geometric cooling factor
+ 'moves_per_temp': 60, # Number of moves at each temperature step
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+ 'cluster_size': 4,
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Place the 26th circle in a strategic corner or center
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9001b32e63ac861c7d70916d3d3d3fa507e5d38
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/original.py
@@ -0,0 +1,275 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This replaces the simpler force-directed approach.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # Phase 1: Main refinement with decreasing growth pressure
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ lr = base_lr * (1.0 - progress)**2
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # Phase 2: Fine-tuning with minimal growth pressure
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
+ spacing = 1.0 / 5
+ candidate_extra_positions = [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ]
+
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ for j in range(5):
+ for i in range(5):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ num_random_starts = self.config['population_size'] - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best solution."""
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
+ best_idx = np.argmax(self.fitnesses)
+ if self.fitnesses[best_idx] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx]
+ self.best_solution = self.population[best_idx].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, taking whole circles from either parent."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a chance of a strong mutation."""
+ mutated_ind = individual.copy()
+ for i in range(self.n):
+ if np.random.rand() < self.config['mutation_rate']:
+ noise = np.random.normal(0, strength, size=2)
+ mutated_ind[i] += noise
+
+ if np.random.rand() < 0.02:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm evolution."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - (gen / self.config['generations']))**2.0
+
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a hybrid Memetic Algorithm.
+ """
+ n = 26
+ config = {
+ # MA parameters
+ 'population_size': 50,
+ 'generations': 250,
+ 'elite_count': 3,
+ 'tournament_size': 4,
+ 'mutation_rate': 0.35,
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+ 'initial_perturbation_scale': 0.01,
+
+ # Memetic Parameters
+ 'local_search_prob': 0.1,
+ 'ls_config': {
+ 'sim_iter': 400,
+ 'radius_sim_iter': 100,
+ 'learning_rate': 0.015,
+ 'wall_strength': 0.6,
+ 'initial_growth_pressure': 1.04,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 200,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ final_radii = compute_max_radii(best_centers, max_iter=2000)
+
+ return best_centers, final_radii
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dae0a4745e71ea76e845a56050e978c0230b308b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_97/rewrite.txt
@@ -0,0 +1,207 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+import math
+import random
+
+def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
+ """
+ Compute maximum radii using iterative proportional scaling.
+ This version is optimized for speed during search, with a final high-precision call.
+ """
+ n = centers.shape[0]
+ if n == 0:
+ return np.array([])
+
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist_sq = np.sum((centers[i] - centers[j])**2)
+ dist = np.sqrt(dist_sq)
+
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9:
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class SimulatedAnnealer:
+ """
+ Performs a search for an optimal circle packing using Simulated Annealing.
+ """
+ def __init__(self, n, initial_centers, config):
+ self.n = n
+ self.config = config
+ self.centers = initial_centers
+
+ self.temp = config['t_start']
+ self.max_step_size = config['max_step_size']
+
+ self.energy = self._calculate_energy(self.centers)
+ self.best_centers = self.centers.copy()
+ self.best_energy = self.energy
+
+ self.move_weights = config['move_weights']
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
+
+ def _calculate_energy(self, centers):
+ """Energy is the negative sum of radii, to be minimized."""
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
+ return -np.sum(radii)
+
+ def _propose_move(self):
+ """
+ Proposes a new state by selecting a move type and applying it.
+ The step size is annealed with the temperature.
+ """
+ # Choose a move type based on weights
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
+
+ # Anneal step size
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
+ current_step_size = self.max_step_size * t_progress
+
+ return move_func(current_step_size)
+
+ def _move_single_circle(self, step_size):
+ """Move a single randomly chosen circle."""
+ new_centers = self.centers.copy()
+ idx = random.randint(0, self.n - 1)
+
+ displacement = np.random.normal(0, step_size, size=2)
+ new_centers[idx] += displacement
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
+ return new_centers
+
+ def _move_cluster(self, step_size):
+ """Move a small cluster of neighboring circles."""
+ new_centers = self.centers.copy()
+ cluster_size = self.config['cluster_size']
+
+ # Pick a seed circle
+ seed_idx = random.randint(0, self.n - 1)
+
+ # Find its neighbors
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
+ neighbor_indices = np.argsort(dists)[:cluster_size]
+
+ # Apply a common displacement
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
+ new_centers[neighbor_indices] += displacement
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
+ return new_centers
+
+ def _swap_circles(self, step_size):
+ """Swap the positions of two randomly chosen circles."""
+ new_centers = self.centers.copy()
+ if self.n < 2: return new_centers
+
+ idx1, idx2 = random.sample(range(self.n), 2)
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
+ return new_centers
+
+ def run(self):
+ """Executes the simulated annealing search."""
+ while self.temp > self.config['t_end']:
+ for _ in range(self.config['moves_per_temp']):
+ # Propose a new configuration
+ new_centers = self._propose_move()
+
+ # Calculate its energy
+ new_energy = self._calculate_energy(new_centers)
+
+ delta_e = new_energy - self.energy
+
+ # Acceptance criterion
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
+ self.centers = new_centers
+ self.energy = new_energy
+
+ # Update best-ever solution
+ if self.energy < self.best_energy:
+ self.best_energy = self.energy
+ self.best_centers = self.centers.copy()
+
+ # Cool down the temperature
+ self.temp *= self.config['cooling_rate']
+
+ return self.best_centers
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
+ """
+ n = 26
+ config = {
+ 'num_starts': 12, # Number of independent SA runs
+ 't_start': 0.05, # Initial temperature
+ 't_end': 1e-6, # Final temperature
+ 'cooling_rate': 0.998, # Geometric cooling factor
+ 'moves_per_temp': 60, # Number of moves at each temperature step
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
+ 'radius_iter': 250, # Radius calculation iterations during search
+ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
+ 'cluster_size': 4,
+ }
+
+ best_overall_centers = None
+ best_overall_score = -1.0
+
+ # Multi-start loop
+ for i in range(config['num_starts']):
+ # --- Generate diverse initial configurations ---
+ initial_centers = np.zeros((n, 2))
+ if i < config['num_starts'] // 2:
+ # Start with a perturbed 5x5 grid + 1 extra circle
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ k = 0
+ for row in range(num_cells_side):
+ for col in range(num_cells_side):
+ initial_centers[k, 0] = (col + 0.5) * spacing
+ initial_centers[k, 1] = (row + 0.5) * spacing
+ k += 1
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
+
+ # Place the 26th circle in a strategic corner or center
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
+ else:
+ # Start with a purely random configuration
+ initial_centers = np.random.rand(n, 2)
+
+ # Run the annealer for this start
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
+ result_centers = solver.run()
+
+ # Evaluate and update the best overall result
+ radii = compute_max_radii(result_centers, max_iter=2000)
+ score = np.sum(radii)
+
+ if score > best_overall_score:
+ best_overall_score = score
+ best_overall_centers = result_centers
+
+ # Final high-precision radius calculation for the best solution found
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
+
+ return best_overall_centers, final_radii
+
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a5ff3e1bb72da58d3b340ad623bf8856f9e70577
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..f766ba6222e4ca3f5254c6601389ec4a6547476e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/edit.diff
@@ -0,0 +1,324 @@
+--- a/original.py
++++ b/original.py
+@@ -1,308 +1,308 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ Vectorized calculation of initial radii based on wall distance.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iterative scaling for inter-circle distances
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+ class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This replaces simpler force-directed approaches by incorporating a "growth pressure"
+ concept adapted from the current program's core logic.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # --- Phase 1: Main refinement with decreasing growth pressure ---
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ lr = base_lr * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-tuning with minimal growth pressure ---
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+ class MemeticAlgorithm:
+ """
+ Combines a Genetic Algorithm for global exploration with a powerful local search meme
+ for exploitation, addressing the limitations of a pure multi-start local search.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """
+ Initializes a diverse population using perturbed grid-based starts and random individuals.
+ The perturbation is critical for breaking symmetry and escaping the local optima of a perfect grid.
+ """
+ pop_size = self.config['population_size']
+
+ # --- Strategic Grid-Based Initialization ---
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ grid_dim = 5
+ spacing = 1.0 / grid_dim
+ for j in range(grid_dim):
+ for i in range(grid_dim):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = self.config['initial_candidates']
+ num_strategic_starts = min(len(candidate_extra_positions), pop_size // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Critical step: Add perturbation to the base grid to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Add the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # --- Random Initialization ---
+ num_random_starts = pop_size - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best overall solution."""
+ fitnesses = [np.sum(compute_max_radii(ind)) for ind in self.population]
+ self.fitnesses = np.array(fitnesses)
+ best_idx_current_gen = np.argmax(self.fitnesses)
+
+ if self.fitnesses[best_idx_current_gen] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx_current_gen]
+ self.best_solution = self.population[best_idx_current_gen].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, swapping entire circles between parents."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a small chance of a large "jump" mutation."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals from the current population
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength over generations
+ progress = gen / self.config['generations']
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - progress)**2.0
+
+ # Generate the rest of the new population through selection, crossover, mutation, and local search
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to capture the best from the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles by evolving a population of solutions
+ using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+- 'population_size': 60,
+- 'generations': 300,
+- 'elite_count': 4,
+- 'tournament_size': 5,
+- 'mutation_rate': 0.4, # Per-circle mutation probability
+- 'jump_mutation_prob': 0.02, # Chance to reset one circle's position
++ 'population_size': 80,
++ 'generations': 350,
++ 'elite_count': 5,
++ 'tournament_size': 6,
++ 'mutation_rate': 0.45, # Per-circle mutation probability
++ 'jump_mutation_prob': 0.03, # Chance to reset one circle's position
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+
+ # Initialization parameters
+- 'initial_perturbation_scale': 0.02, # Critical for breaking grid symmetry
++ 'initial_perturbation_scale': 0.02,
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search)
+- 'local_search_prob': 0.15,
++ 'local_search_prob': 0.20,
+ 'ls_config': {
+- 'sim_iter': 400,
++ 'sim_iter': 500,
+ 'radius_sim_iter': 150,
+- 'learning_rate': 0.02,
+- 'wall_strength': 0.7,
+- 'initial_growth_pressure': 1.045,
++ 'learning_rate': 0.018,
++ 'wall_strength': 0.65,
++ 'initial_growth_pressure': 1.05,
+ 'final_growth_pressure': 1.001,
+- 'fine_tune_iter': 200,
++ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best found solution
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a3deffc2b4eecf82fdbf349098c380192642cad
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/main.py
@@ -0,0 +1,308 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ Vectorized calculation of initial radii based on wall distance.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iterative scaling for inter-circle distances
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This replaces simpler force-directed approaches by incorporating a "growth pressure"
+ concept adapted from the current program's core logic.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # --- Phase 1: Main refinement with decreasing growth pressure ---
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ lr = base_lr * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-tuning with minimal growth pressure ---
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """
+ Combines a Genetic Algorithm for global exploration with a powerful local search meme
+ for exploitation, addressing the limitations of a pure multi-start local search.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """
+ Initializes a diverse population using perturbed grid-based starts and random individuals.
+ The perturbation is critical for breaking symmetry and escaping the local optima of a perfect grid.
+ """
+ pop_size = self.config['population_size']
+
+ # --- Strategic Grid-Based Initialization ---
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ grid_dim = 5
+ spacing = 1.0 / grid_dim
+ for j in range(grid_dim):
+ for i in range(grid_dim):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = self.config['initial_candidates']
+ num_strategic_starts = min(len(candidate_extra_positions), pop_size // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Critical step: Add perturbation to the base grid to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Add the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # --- Random Initialization ---
+ num_random_starts = pop_size - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best overall solution."""
+ fitnesses = [np.sum(compute_max_radii(ind)) for ind in self.population]
+ self.fitnesses = np.array(fitnesses)
+ best_idx_current_gen = np.argmax(self.fitnesses)
+
+ if self.fitnesses[best_idx_current_gen] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx_current_gen]
+ self.best_solution = self.population[best_idx_current_gen].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, swapping entire circles between parents."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a small chance of a large "jump" mutation."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals from the current population
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength over generations
+ progress = gen / self.config['generations']
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - progress)**2.0
+
+ # Generate the rest of the new population through selection, crossover, mutation, and local search
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to capture the best from the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by evolving a population of solutions
+ using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 5,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.45, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.03, # Chance to reset one circle's position
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+
+ # Initialization parameters
+ 'initial_perturbation_scale': 0.02,
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search)
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+ 'sim_iter': 500,
+ 'radius_sim_iter': 150,
+ 'learning_rate': 0.018,
+ 'wall_strength': 0.65,
+ 'initial_growth_pressure': 1.05,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best found solution
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..f064cf78633a9edcc82e7ceb38bef4d405b96bce
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/original.py
@@ -0,0 +1,308 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ Vectorized calculation of initial radii based on wall distance.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iterative scaling for inter-circle distances
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This replaces simpler force-directed approaches by incorporating a "growth pressure"
+ concept adapted from the current program's core logic.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # --- Phase 1: Main refinement with decreasing growth pressure ---
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ lr = base_lr * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-tuning with minimal growth pressure ---
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """
+ Combines a Genetic Algorithm for global exploration with a powerful local search meme
+ for exploitation, addressing the limitations of a pure multi-start local search.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """
+ Initializes a diverse population using perturbed grid-based starts and random individuals.
+ The perturbation is critical for breaking symmetry and escaping the local optima of a perfect grid.
+ """
+ pop_size = self.config['population_size']
+
+ # --- Strategic Grid-Based Initialization ---
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ grid_dim = 5
+ spacing = 1.0 / grid_dim
+ for j in range(grid_dim):
+ for i in range(grid_dim):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = self.config['initial_candidates']
+ num_strategic_starts = min(len(candidate_extra_positions), pop_size // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Critical step: Add perturbation to the base grid to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Add the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # --- Random Initialization ---
+ num_random_starts = pop_size - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best overall solution."""
+ fitnesses = [np.sum(compute_max_radii(ind)) for ind in self.population]
+ self.fitnesses = np.array(fitnesses)
+ best_idx_current_gen = np.argmax(self.fitnesses)
+
+ if self.fitnesses[best_idx_current_gen] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx_current_gen]
+ self.best_solution = self.population[best_idx_current_gen].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, swapping entire circles between parents."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a small chance of a large "jump" mutation."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals from the current population
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength over generations
+ progress = gen / self.config['generations']
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - progress)**2.0
+
+ # Generate the rest of the new population through selection, crossover, mutation, and local search
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to capture the best from the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by evolving a population of solutions
+ using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+ 'population_size': 60,
+ 'generations': 300,
+ 'elite_count': 4,
+ 'tournament_size': 5,
+ 'mutation_rate': 0.4, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.02, # Chance to reset one circle's position
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+
+ # Initialization parameters
+ 'initial_perturbation_scale': 0.02, # Critical for breaking grid symmetry
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search)
+ 'local_search_prob': 0.15,
+ 'ls_config': {
+ 'sim_iter': 400,
+ 'radius_sim_iter': 150,
+ 'learning_rate': 0.02,
+ 'wall_strength': 0.7,
+ 'initial_growth_pressure': 1.045,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 200,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best found solution
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..8d06d2f7093ee4f36b977a0cf3f76872ac4ef133
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results
+Run 1/1 completed in 8494.88 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.3137564112220823
+ public: {'centers_str': ' centers[0] = (0.0838, 0.0845)\n centers[1] = (0.1939, 0.0373)\n centers[2] = (0.5860, 0.0495)\n centers[3] = (0.8545, 0.0253)\n centers[4] = (0.9337, 0.0703)\n centers[5] = (0.0618, 0.2286)\n centers[6] = (0.4127, 0.6659)\n centers[7] = (0.6145, 0.4214)\n centers[8] = (0.7424, 0.1325)\n centers[9] = (0.9126, 0.2696)\n centers[10] = (0.0652, 0.3551)\n centers[11] = (0.1084, 0.5214)\n centers[12] = (0.4657, 0.5747)\n centers[13] = (0.7549, 0.3624)\n centers[14] = (0.9015, 0.4914)\n centers[15] = (0.1029, 0.7323)\n centers[16] = (0.2933, 0.6354)\n centers[17] = (0.4827, 0.7478)\n centers[18] = (0.6804, 0.5883)\n centers[19] = (0.8947, 0.7248)\n centers[20] = (0.0825, 0.9190)\n centers[21] = (0.2839, 0.8749)\n centers[22] = (0.4825, 0.9268)\n centers[23] = (0.6991, 0.8665)\n centers[24] = (0.9130, 0.9147)\n centers[25] = (0.3794, 0.2607)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.3137564112220823}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/packing_viz.png
+ execution_time_mean: 8494.881229057908
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..066fa13fe7be6c27950bdc210ddb01637a1314d3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.3137564112220823,
+ "public": {
+ "centers_str": " centers[0] = (0.0838, 0.0845)\n centers[1] = (0.1939, 0.0373)\n centers[2] = (0.5860, 0.0495)\n centers[3] = (0.8545, 0.0253)\n centers[4] = (0.9337, 0.0703)\n centers[5] = (0.0618, 0.2286)\n centers[6] = (0.4127, 0.6659)\n centers[7] = (0.6145, 0.4214)\n centers[8] = (0.7424, 0.1325)\n centers[9] = (0.9126, 0.2696)\n centers[10] = (0.0652, 0.3551)\n centers[11] = (0.1084, 0.5214)\n centers[12] = (0.4657, 0.5747)\n centers[13] = (0.7549, 0.3624)\n centers[14] = (0.9015, 0.4914)\n centers[15] = (0.1029, 0.7323)\n centers[16] = (0.2933, 0.6354)\n centers[17] = (0.4827, 0.7478)\n centers[18] = (0.6804, 0.5883)\n centers[19] = (0.8947, 0.7248)\n centers[20] = (0.0825, 0.9190)\n centers[21] = (0.2839, 0.8749)\n centers[22] = (0.4825, 0.9268)\n centers[23] = (0.6991, 0.8665)\n centers[24] = (0.9130, 0.9147)\n centers[25] = (0.3794, 0.2607)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.3137564112220823
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/results/packing_viz.png",
+ "execution_time_mean": 8494.881229057908,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/rewrite.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/rewrite.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3498682434a5053699626a157339e777a01fb1c2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_98/rewrite.txt
@@ -0,0 +1,299 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
+ """
+ Compute maximum radii using iterative proportional scaling with a convergence check.
+ Vectorized calculation of initial radii based on wall distance.
+ """
+ n = centers.shape[0]
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ # Iterative scaling for inter-circle distances
+ for _ in range(max_iter):
+ old_radii = radii.copy()
+ updated = False
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+ if radii[i] + radii[j] > dist:
+ if dist < 1e-9: # Handle co-located centers
+ radii[i], radii[j] = 0.0, 0.0
+ else:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated = True
+
+ # Check for convergence
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
+ break
+
+ return np.maximum(radii, 0)
+
+
+class HybridLocalSearcher:
+ """
+ A powerful local search refiner based on a two-phase physical simulation.
+ This replaces simpler force-directed approaches by incorporating a "growth pressure"
+ concept adapted from the current program's core logic.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+
+ def _compute_radii_for_sim(self, centers):
+ """A fast radius computation for use inside the simulation loop."""
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
+
+ def refine(self, centers):
+ """Applies a two-phase physical simulation to refine a configuration."""
+ refined_centers = centers.copy()
+
+ # --- Phase 1: Main refinement with decreasing growth pressure ---
+ iterations = self.config['sim_iter']
+ base_lr = self.config['learning_rate']
+ wall_strength = self.config['wall_strength']
+ initial_growth_pressure = self.config['initial_growth_pressure']
+ final_growth_pressure = self.config['final_growth_pressure']
+
+ for i_iter in range(iterations):
+ progress = i_iter / iterations
+ # Anneal growth pressure and learning rate quadratically
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+ lr = base_lr * (1.0 - progress)**2
+
+ current_radii = self._compute_radii_for_sim(refined_centers)
+ pressured_radii = current_radii * current_growth_pressure
+
+ # Vectorized force calculations
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-tuning with minimal growth pressure ---
+ iterations_ft = self.config['fine_tune_iter']
+ lr_ft = self.config['fine_tune_lr']
+ for _ in range(iterations_ft):
+ current_radii = self._compute_radii_for_sim(refined_centers)
+
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = np.maximum(0, radii_sums - dists)
+ np.fill_diagonal(overlaps, 0)
+
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
+ circle_forces = np.sum(force_matrix, axis=1)
+
+ wall_forces = np.zeros_like(refined_centers)
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
+
+ forces = circle_forces + wall_forces
+ refined_centers += forces * lr_ft
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
+
+ return refined_centers
+
+
+class MemeticAlgorithm:
+ """
+ Combines a Genetic Algorithm for global exploration with a powerful local search meme
+ for exploitation, addressing the limitations of a pure multi-start local search.
+ """
+ def __init__(self, n, config):
+ self.n = n
+ self.config = config
+ self.population = []
+ self.fitnesses = np.array([])
+ self.best_solution = None
+ self.best_fitness = -1.0
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
+
+ def _initialize_population(self):
+ """
+ Initializes a diverse population using perturbed grid-based starts and random individuals.
+ The perturbation is critical for breaking symmetry and escaping the local optima of a perfect grid.
+ """
+ pop_size = self.config['population_size']
+
+ # --- Strategic Grid-Based Initialization ---
+ base_centers = np.zeros((self.n - 1, 2))
+ k = 0
+ grid_dim = 5
+ spacing = 1.0 / grid_dim
+ for j in range(grid_dim):
+ for i in range(grid_dim):
+ base_centers[k, 0] = (i + 0.5) * spacing
+ base_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+
+ candidate_extra_positions = self.config['initial_candidates']
+ num_strategic_starts = min(len(candidate_extra_positions), pop_size // 2)
+
+ for i in range(num_strategic_starts):
+ centers = np.zeros((self.n, 2))
+ # Critical step: Add perturbation to the base grid to break symmetry
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers.shape)
+ perturbed_base_centers = np.clip(base_centers + perturbation, 0.0, 1.0)
+ centers[:self.n-1] = perturbed_base_centers
+ # Add the 26th circle at a strategic point
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
+ self.population.append(centers)
+
+ # --- Random Initialization ---
+ num_random_starts = pop_size - len(self.population)
+ for _ in range(num_random_starts):
+ self.population.append(np.random.rand(self.n, 2))
+
+ def _evaluate_population(self):
+ """Calculates fitness for the population and updates the best overall solution."""
+ fitnesses = [np.sum(compute_max_radii(ind)) for ind in self.population]
+ self.fitnesses = np.array(fitnesses)
+ best_idx_current_gen = np.argmax(self.fitnesses)
+
+ if self.fitnesses[best_idx_current_gen] > self.best_fitness:
+ self.best_fitness = self.fitnesses[best_idx_current_gen]
+ self.best_solution = self.population[best_idx_current_gen].copy()
+
+ def _select_parent(self):
+ """Selects a parent using tournament selection."""
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
+ tourn_fitnesses = self.fitnesses[tourn_indices]
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
+ return self.population[winner_idx]
+
+ def _crossover(self, p1, p2):
+ """Performs uniform crossover, swapping entire circles between parents."""
+ child = p1.copy()
+ mask = np.random.rand(self.n) < 0.5
+ child[mask] = p2[mask]
+ return child
+
+ def _mutate(self, individual, strength):
+ """Applies Gaussian mutation and a small chance of a large "jump" mutation."""
+ mutated_ind = individual.copy()
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
+ mutated_ind[mutation_mask] += noise
+
+ if np.random.rand() < self.config['jump_mutation_prob']:
+ idx_to_reset = np.random.randint(0, self.n)
+ mutated_ind[idx_to_reset] = np.random.rand(2)
+
+ return np.clip(mutated_ind, 0.0, 1.0)
+
+ def run(self):
+ """Executes the full memetic algorithm."""
+ self._initialize_population()
+
+ for gen in range(self.config['generations']):
+ self._evaluate_population()
+
+ new_population = []
+
+ # Elitism: carry over the best individuals from the current population
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
+ for idx in elite_indices:
+ new_population.append(self.population[idx].copy())
+
+ # Anneal mutation strength over generations
+ progress = gen / self.config['generations']
+ mut_strength = self.config['mut_strength_end'] + \
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
+ (1.0 - progress)**2.0
+
+ # Generate the rest of the new population through selection, crossover, mutation, and local search
+ while len(new_population) < self.config['population_size']:
+ p1 = self._select_parent()
+ p2 = self._select_parent()
+
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
+ child = self._mutate(child, mut_strength)
+
+ # Memetic step: Apply powerful local search probabilistically
+ if np.random.rand() < self.config['local_search_prob']:
+ child = self.local_search_refiner.refine(child)
+
+ new_population.append(child)
+
+ self.population = new_population
+
+ # Final evaluation to capture the best from the last generation
+ self._evaluate_population()
+ return self.best_solution
+
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles by evolving a population of solutions
+ using a Memetic Algorithm.
+ """
+ n = 26
+ spacing = 1.0 / 5
+ config = {
+ # MA parameters
+ 'population_size': 80,
+ 'generations': 350,
+ 'elite_count': 5,
+ 'tournament_size': 6,
+ 'mutation_rate': 0.45, # Per-circle mutation probability
+ 'jump_mutation_prob': 0.03, # Chance to reset one circle's position
+ 'mut_strength_start': 0.1,
+ 'mut_strength_end': 0.001,
+ 'crossover_rate': 0.9,
+
+ # Initialization parameters
+ 'initial_perturbation_scale': 0.02,
+ 'initial_candidates': [
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
+ ],
+
+ # Memetic Parameters (Local Search)
+ 'local_search_prob': 0.20,
+ 'ls_config': {
+ 'sim_iter': 500,
+ 'radius_sim_iter': 150,
+ 'learning_rate': 0.018,
+ 'wall_strength': 0.65,
+ 'initial_growth_pressure': 1.05,
+ 'final_growth_pressure': 1.001,
+ 'fine_tune_iter': 250,
+ 'fine_tune_lr': 0.0015,
+ }
+ }
+
+ solver = MemeticAlgorithm(n=n, config=config)
+ best_centers = solver.run()
+
+ # Final, high-precision radius calculation for the best found solution
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
+
+ return best_centers, final_radii
+# EVOLVE-BLOCK-END
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2555d4d8d5053ad07ff9610803f026c0fd234e46
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/edit.diff b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..965be3a70fc3dd05ac2a401bc35cb48c3f297b1f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/edit.diff
@@ -0,0 +1,272 @@
+--- a/original.py
++++ b/original.py
+@@ -1,248 +1,254 @@
+ # EVOLVE-BLOCK-START
+ import numpy as np
+
+ def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+-def hybrid_local_searcher(centers, sim_iter=120, fine_tune_iter=30, radius_sim_iter=35):
++def hybrid_local_searcher(centers, sim_iter=250, fine_tune_iter=80, radius_sim_iter=80):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+- initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
++ initial_growth_pressure = 1.03 # Agressive but controlled pressure
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+ class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+ def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+- perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
++ perturbed_centers = grid_centers + np.random.normal(0, 0.02, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+ def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+ def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+ def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+ def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 80
+- NUM_GENERATIONS = 300
+- MUTATION_RATE = 0.15
+- INITIAL_MUTATION_STRENGTH = 0.06
++ NUM_GENERATIONS = 500
++ MUTATION_RATE = 0.20
++ INITIAL_MUTATION_STRENGTH = 0.05
+ FINAL_MUTATION_STRENGTH = 0.001
+- TOURNAMENT_SIZE = 5
+- ELITISM_COUNT = 4
+- LOCAL_SEARCH_PROB = 0.7 # Probability of applying local search to a new child
+-
++ TOURNAMENT_SIZE = 7
++ ELITISM_COUNT = 5
++ LOCAL_SEARCH_PROB = 0.15 # Probability of applying local search to a new child
++
++ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+- # Initialize and optimize the entire starting population
++ # Initialize population by evaluating fitness (no premature local search)
+ for individual in population:
+- individual.centers = hybrid_local_searcher(individual.centers.copy())
+- individual.evaluate_fitness(FINAL_RADIUS_ITER)
++ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+- child.evaluate_fitness(FINAL_RADIUS_ITER)
++ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+- best_individual_overall = current_best_in_gen
++ # Copy to prevent modification of the best individual if it is an elite
++ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
++ best_individual_overall.fitness = current_best_in_gen.fitness
++
++ # Final polish on the best solution found with an intensive local search
++ best_individual_overall.centers = hybrid_local_searcher(best_individual_overall.centers.copy(), sim_iter=600, fine_tune_iter=250, radius_sim_iter=200)
++ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/main.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c50bf31d25edf12b92b3208b810fdd15fa96184
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/main.py
@@ -0,0 +1,254 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=250, fine_tune_iter=80, radius_sim_iter=80):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.03 # Agressive but controlled pressure
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.02, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 500
+ MUTATION_RATE = 0.20
+ INITIAL_MUTATION_STRENGTH = 0.05
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7
+ ELITISM_COUNT = 5
+ LOCAL_SEARCH_PROB = 0.15 # Probability of applying local search to a new child
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize population by evaluating fitness (no premature local search)
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Copy to prevent modification of the best individual if it is an elite
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+
+ # Final polish on the best solution found with an intensive local search
+ best_individual_overall.centers = hybrid_local_searcher(best_individual_overall.centers.copy(), sim_iter=600, fine_tune_iter=250, radius_sim_iter=200)
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/original.py b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..f43e03b099c1fc4d99709630376f0aab6b5dad92
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/original.py
@@ -0,0 +1,248 @@
+# EVOLVE-BLOCK-START
+import numpy as np
+
+def compute_max_radii(centers, iterations=500):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+ This uses an iterative proportional scaling method with convergence check.
+ """
+ n = centers.shape[0]
+ # Initialize radii based on distance to walls, which is the hard upper limit.
+ radii = np.min([
+ centers[:, 0], 1 - centers[:, 0],
+ centers[:, 1], 1 - centers[:, 1]
+ ], axis=0)
+
+ convergence_epsilon = 1e-9 # A small threshold to check for stabilization
+
+ for _ in range(iterations):
+ updated_in_pass = False
+ previous_radii = radii.copy()
+
+ # Iteratively shrink radii that cause overlaps
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.linalg.norm(centers[i] - centers[j])
+
+ if dist < 1e-9: # Handle co-located centers
+ if radii[i] > 0 or radii[j] > 0:
+ radii[i], radii[j] = 0.0, 0.0
+ updated_in_pass = True
+ continue
+
+ if radii[i] + radii[j] > dist:
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+ updated_in_pass = True
+
+ # Check for convergence
+ max_delta_r = np.max(np.abs(radii - previous_radii))
+ if not updated_in_pass and max_delta_r < convergence_epsilon:
+ break
+
+ return np.maximum(radii, 0.0)
+
+def hybrid_local_searcher(centers, sim_iter=120, fine_tune_iter=30, radius_sim_iter=35):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+
+ # --- Phase 1: Refinement with Annealed Growth Pressure ---
+ for i_iter in range(sim_iter):
+ progress = i_iter / sim_iter
+ current_growth_pressure = final_growth_pressure + \
+ (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
+
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ pressured_radii = current_radii * current_growth_pressure
+
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion
+ forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + pressured_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + pressured_radii) - 1.0)
+
+ lr = learning_rate * (1.0 - progress)**2
+ centers += forces * lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ # --- Phase 2: Fine-Tuning without Growth Pressure ---
+ for _ in range(fine_tune_iter):
+ current_radii = compute_max_radii(centers, iterations=radius_sim_iter)
+ forces = np.zeros_like(centers)
+
+ # Vectorized circle-circle repulsion (based on actual overlaps)
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
+ dists[dists < 1e-9] = 1e-9
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
+ overlaps = radii_sums - dists
+ overlaps[overlaps < 0] = 0.0
+ np.fill_diagonal(overlaps, 0)
+ force_magnitudes = overlaps
+ force_matrix = diffs * (force_magnitudes / dists)[:, :, np.newaxis]
+ forces += np.sum(force_matrix, axis=1)
+
+ # Vectorized wall repulsion (based on actual radii)
+ forces[:, 0] += wall_strength * np.maximum(0, current_radii - centers[:, 0])
+ forces[:, 0] -= wall_strength * np.maximum(0, (centers[:, 0] + current_radii) - 1.0)
+ forces[:, 1] += wall_strength * np.maximum(0, current_radii - centers[:, 1])
+ forces[:, 1] -= wall_strength * np.maximum(0, (centers[:, 1] + current_radii) - 1.0)
+
+ centers += forces * fine_tune_lr
+ centers = np.clip(centers, 0.0, 1.0)
+
+ return centers
+
+class Individual:
+ """Represents a single candidate packing solution."""
+ def __init__(self, centers_array, n_circles):
+ self.centers = np.array(centers_array).reshape(n_circles, 2)
+ self.radii = None
+ self.fitness = -1.0
+
+ def evaluate_fitness(self, radius_iter_val):
+ self.radii = compute_max_radii(self.centers, iterations=radius_iter_val)
+ self.fitness = np.sum(self.radii)
+ return self.fitness
+
+def initialize_population(n_circles, population_size):
+ """Initializes a diverse population including grid-based and random individuals."""
+ population = []
+ # 1. Add a known good starting grid (5x5 + 1)
+ num_cells_side = 5
+ spacing = 1.0 / num_cells_side
+ grid_centers = np.zeros((n_circles, 2))
+ k = 0
+ for j in range(num_cells_side):
+ for i in range(num_cells_side):
+ grid_centers[k, 0] = (i + 0.5) * spacing
+ grid_centers[k, 1] = (j + 0.5) * spacing
+ k += 1
+ grid_centers[n_circles - 1] = [spacing, spacing]
+ population.append(Individual(grid_centers, n_circles))
+
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+
+ # 3. Fill with random individuals for diversity
+ while len(population) < population_size:
+ population.append(Individual(np.random.rand(n_circles, 2), n_circles))
+
+ return population
+
+def select_parents(population, tournament_size):
+ """Selects two distinct parents using tournament selection."""
+ p1 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ while True:
+ p2 = max(np.random.choice(population, tournament_size, replace=False), key=lambda x: x.fitness)
+ if p2 is not p1: return p1, p2
+
+def crossover(parent1, parent2, n_circles):
+ """Performs uniform crossover on the circle centers."""
+ child_centers = np.where(np.random.rand(n_circles, 1) < 0.5, parent1.centers, parent2.centers)
+ return Individual(child_centers, n_circles)
+
+def mutate(individual, mutation_rate, mutation_strength, n_circles):
+ """Applies Gaussian noise to circle centers."""
+ mask = np.random.rand(n_circles) < mutation_rate
+ noise = np.random.normal(0, mutation_strength, (n_circles, 2))
+ individual.centers[mask] += noise[mask]
+ individual.centers = np.clip(individual.centers, 0.0, 1.0)
+ return individual
+
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 300
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 5
+ ELITISM_COUNT = 4
+ LOCAL_SEARCH_PROB = 0.7 # Probability of applying local search to a new child
+
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(FINAL_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..eba11b5d3ce43635686b54bba0325ccc026784db
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/main.py
+Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results
+Run 1/1 completed in 4829.19 seconds
+Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/extra.npz
+Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/correct.json
+Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.2748733796637626
+ public: {'centers_str': ' centers[0] = (0.0620, 0.0622)\n centers[1] = (0.4678, 0.0355)\n centers[2] = (0.5367, 0.0318)\n centers[3] = (0.7948, 0.0504)\n centers[4] = (0.9213, 0.0786)\n centers[5] = (0.0484, 0.3029)\n centers[6] = (0.4490, 0.6316)\n centers[7] = (0.5032, 0.3766)\n centers[8] = (0.6416, 0.1588)\n centers[9] = (0.8759, 0.2771)\n centers[10] = (0.0929, 0.4435)\n centers[11] = (0.2975, 0.5178)\n centers[12] = (0.5323, 0.5770)\n centers[13] = (0.6715, 0.4162)\n centers[14] = (0.8920, 0.5100)\n centers[15] = (0.1022, 0.6395)\n centers[16] = (0.3068, 0.7332)\n centers[17] = (0.4821, 0.6953)\n centers[18] = (0.6991, 0.6970)\n centers[19] = (0.9297, 0.7019)\n centers[20] = (0.1178, 0.8818)\n centers[21] = (0.3082, 0.9281)\n centers[22] = (0.4943, 0.8836)\n centers[23] = (0.6950, 0.9286)\n centers[24] = (0.8892, 0.8895)\n centers[25] = (0.2941, 0.2121)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.2748733796637626}
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/packing_viz.png
+ execution_time_mean: 4829.191572040785
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..0a6f64b0578b95c7418a52ba8370e606a275a3f3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.2748733796637626,
+ "public": {
+ "centers_str": " centers[0] = (0.0620, 0.0622)\n centers[1] = (0.4678, 0.0355)\n centers[2] = (0.5367, 0.0318)\n centers[3] = (0.7948, 0.0504)\n centers[4] = (0.9213, 0.0786)\n centers[5] = (0.0484, 0.3029)\n centers[6] = (0.4490, 0.6316)\n centers[7] = (0.5032, 0.3766)\n centers[8] = (0.6416, 0.1588)\n centers[9] = (0.8759, 0.2771)\n centers[10] = (0.0929, 0.4435)\n centers[11] = (0.2975, 0.5178)\n centers[12] = (0.5323, 0.5770)\n centers[13] = (0.6715, 0.4162)\n centers[14] = (0.8920, 0.5100)\n centers[15] = (0.1022, 0.6395)\n centers[16] = (0.3068, 0.7332)\n centers[17] = (0.4821, 0.6953)\n centers[18] = (0.6991, 0.6970)\n centers[19] = (0.9297, 0.7019)\n centers[20] = (0.1178, 0.8818)\n centers[21] = (0.3082, 0.9281)\n centers[22] = (0.4943, 0.8836)\n centers[23] = (0.6950, 0.9286)\n centers[24] = (0.8892, 0.8895)\n centers[25] = (0.2941, 0.2121)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.2748733796637626
+ },
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/results/packing_viz.png",
+ "execution_time_mean": 4829.191572040785,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/search_replace.txt b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f2e7c0047856e4c47cbd2a2c397dc6e148a49e11
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_99/search_replace.txt
@@ -0,0 +1,197 @@
+
+strengthen_local_search
+
+
+The current `hybrid_local_searcher` has low iteration counts (`sim_iter=120`, `fine_tune_iter=30`), making it a weak refiner compared to previous successful versions (e.g., G90). This change significantly increases the iteration counts for both the main simulation and fine-tuning phases, and also for the radius calculations within the simulation. This gives the force-directed algorithm more time to settle the circles into a more optimal, tightly-packed configuration. I also slightly reduce the initial growth pressure from 1.05 to 1.03, aligning with previously successful parameters to prevent an overly chaotic initial expansion.
+
+
+<<<<<<< SEARCH
+def hybrid_local_searcher(centers, sim_iter=120, fine_tune_iter=30, radius_sim_iter=35):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.05 # Aggressive pressure to escape local minima
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+=======
+def hybrid_local_searcher(centers, sim_iter=250, fine_tune_iter=80, radius_sim_iter=80):
+ """
+ Refines circle centers using a two-phase force-directed simulation. This acts as
+ the local search operator for the Memetic Algorithm.
+ """
+ n = centers.shape[0]
+
+ # Tuned hyperparameters for fast but effective local optimization
+ learning_rate = 0.02
+ wall_strength = 0.8
+ initial_growth_pressure = 1.03 # Agressive but controlled pressure
+ final_growth_pressure = 1.001
+ fine_tune_lr = 0.001
+>>>>>>> REPLACE
+
+
+refine_initial_population
+
+
+The initial population seeds the entire genetic search. The current implementation uses a fairly large perturbation (`0.03`) on the grid-based starting configurations. This can make the initial individuals too chaotic, moving them far from the potentially good structure of the grid. This change reduces the perturbation strength to `0.02`, creating starting points that are still diverse but closer to the well-structured grid, providing better "raw material" for the genetic algorithm to work with.
+
+
+<<<<<<< SEARCH
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.03, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+=======
+ # 2. Add perturbed versions of the grid
+ for _ in range(population_size // 5):
+ perturbed_centers = grid_centers + np.random.normal(0, 0.02, (n_circles, 2))
+ population.append(Individual(np.clip(perturbed_centers, 0.0, 1.0), n_circles))
+>>>>>>> REPLACE
+
+
+overhaul_memetic_strategy
+
+
+The current Memetic Algorithm (MA) has several strategic weaknesses compared to the best-performing prior versions. It applies a weak local search to the entire initial population, risking premature convergence. It also uses a high probability (0.7) for local search and evaluates every individual with high precision, making each generation very slow.
+
+This change overhauls the MA strategy:
+1. **Introduce `GA_RADIUS_ITER`**: Fitness evaluation inside the GA loop is now done with fewer iterations for speed, allowing for more generations.
+2. **Adjust Hyperparameters**: `NUM_GENERATIONS` is increased to 500, `LOCAL_SEARCH_PROB` is reduced to a more balanced 0.15, and selection pressure is increased.
+3. **Preserve Initial Diversity**: Local search is no longer applied to the initial population, allowing the GA to explore a wider range of solutions.
+4. **Add Final Polish**: A final, intensive local search is applied to the best individual found by the GA, ensuring the final result is as refined as possible.
+
+These changes revert to a proven, more effective MA structure, balancing global exploration (GA) with powerful, targeted local exploitation (local search).
+
+
+<<<<<<< SEARCH
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 300
+ MUTATION_RATE = 0.15
+ INITIAL_MUTATION_STRENGTH = 0.06
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 5
+ ELITISM_COUNT = 4
+ LOCAL_SEARCH_PROB = 0.7 # Probability of applying local search to a new child
+
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize and optimize the entire starting population
+ for individual in population:
+ individual.centers = hybrid_local_searcher(individual.centers.copy())
+ individual.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(FINAL_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ best_individual_overall = current_best_in_gen
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+=======
+def construct_packing():
+ """
+ Constructs a packing of 26 circles using a Memetic Algorithm.
+ """
+ n_circles = 26
+
+ # --- Memetic Algorithm Hyperparameters ---
+ POPULATION_SIZE = 80
+ NUM_GENERATIONS = 500
+ MUTATION_RATE = 0.20
+ INITIAL_MUTATION_STRENGTH = 0.05
+ FINAL_MUTATION_STRENGTH = 0.001
+ TOURNAMENT_SIZE = 7
+ ELITISM_COUNT = 5
+ LOCAL_SEARCH_PROB = 0.15 # Probability of applying local search to a new child
+
+ GA_RADIUS_ITER = 100
+ FINAL_RADIUS_ITER = 500
+
+ population = initialize_population(n_circles, POPULATION_SIZE)
+
+ # Initialize population by evaluating fitness (no premature local search)
+ for individual in population:
+ individual.evaluate_fitness(GA_RADIUS_ITER)
+
+ best_individual_overall = max(population, key=lambda ind: ind.fitness)
+
+ # --- Main MA Loop ---
+ for generation in range(NUM_GENERATIONS):
+ progress = generation / NUM_GENERATIONS
+ current_mutation_strength = FINAL_MUTATION_STRENGTH + \
+ (INITIAL_MUTATION_STRENGTH - FINAL_MUTATION_STRENGTH) * (1.0 - progress)**2
+
+ new_population = []
+
+ sorted_population = sorted(population, key=lambda ind: ind.fitness, reverse=True)
+ new_population.extend(sorted_population[:ELITISM_COUNT])
+
+ while len(new_population) < POPULATION_SIZE:
+ parent1, parent2 = select_parents(population, TOURNAMENT_SIZE)
+ child = crossover(parent1, parent2, n_circles)
+ child = mutate(child, MUTATION_RATE, current_mutation_strength, n_circles)
+
+ # --- Memetic Step: Apply Local Search ---
+ if np.random.rand() < LOCAL_SEARCH_PROB:
+ child.centers = hybrid_local_searcher(child.centers.copy())
+
+ child.evaluate_fitness(GA_RADIUS_ITER)
+ new_population.append(child)
+
+ population = new_population
+
+ current_best_in_gen = max(population, key=lambda ind: ind.fitness)
+ if current_best_in_gen.fitness > best_individual_overall.fitness:
+ # Copy to prevent modification of the best individual if it is an elite
+ best_individual_overall = Individual(current_best_in_gen.centers.copy(), n_circles)
+ best_individual_overall.fitness = current_best_in_gen.fitness
+
+ # Final polish on the best solution found with an intensive local search
+ best_individual_overall.centers = hybrid_local_searcher(best_individual_overall.centers.copy(), sim_iter=600, fine_tune_iter=250, radius_sim_iter=200)
+ best_individual_overall.evaluate_fitness(FINAL_RADIUS_ITER)
+
+ # Return the best solution found
+ return best_individual_overall.centers, best_individual_overall.radii
+>>>>>>> REPLACE
+
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..87ffd557dd0eebbd354fcfc5958e60dfa1d68a74
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..963bcc93363410e38283c413191101551937a9a3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_12/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_12/results
+Run 1/1 completed in 23.97 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_12/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_12/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_12/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_12/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.8403523400947381
+ public: {'centers_str': ' centers[0] = (0.1016, 0.0793)\n centers[1] = (0.1072, 0.2632)\n centers[2] = (0.0563, 0.4484)\n centers[3] = (0.1007, 0.6024)\n centers[4] = (0.0585, 0.7467)\n centers[5] = (0.0747, 0.9451)\n centers[6] = (0.2596, 0.1049)\n centers[7] = (0.2462, 0.2318)\n centers[8] = (0.2536, 0.3876)\n centers[9] = (0.2718, 0.5921)\n centers[10] = (0.2672, 0.7403)\n centers[11] = (0.2814, 0.9429)\n centers[12] = (0.4352, 0.0630)\n centers[13] = (0.4144, 0.2196)\n centers[14] = (0.3936, 0.4289)\n centers[15] = (0.4330, 0.6145)\n centers[16] = (0.4051, 0.7414)\n centers[17] = (0.4146, 0.8960)\n centers[18] = (0.5587, 0.0817)\n centers[19] = (0.5651, 0.2613)\n centers[20] = (0.5791, 0.4388)\n centers[21] = (0.5967, 0.5708)\n centers[22] = (0.6055, 0.7703)\n centers[23] = (0.5758, 0.9026)\n centers[24] = (0.7622, 0.0593)\n centers[25] = (0.7300, 0.2172)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.8403523400947381}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_12/results/packing_viz.png
+ execution_time_mean: 23.97458342416212
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..255bc2c3b0cd003bc78e5d520384e870a3b1e300
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_12/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.8403523400947381,
+ "public": {
+ "centers_str": " centers[0] = (0.1016, 0.0793)\n centers[1] = (0.1072, 0.2632)\n centers[2] = (0.0563, 0.4484)\n centers[3] = (0.1007, 0.6024)\n centers[4] = (0.0585, 0.7467)\n centers[5] = (0.0747, 0.9451)\n centers[6] = (0.2596, 0.1049)\n centers[7] = (0.2462, 0.2318)\n centers[8] = (0.2536, 0.3876)\n centers[9] = (0.2718, 0.5921)\n centers[10] = (0.2672, 0.7403)\n centers[11] = (0.2814, 0.9429)\n centers[12] = (0.4352, 0.0630)\n centers[13] = (0.4144, 0.2196)\n centers[14] = (0.3936, 0.4289)\n centers[15] = (0.4330, 0.6145)\n centers[16] = (0.4051, 0.7414)\n centers[17] = (0.4146, 0.8960)\n centers[18] = (0.5587, 0.0817)\n centers[19] = (0.5651, 0.2613)\n centers[20] = (0.5791, 0.4388)\n centers[21] = (0.5967, 0.5708)\n centers[22] = (0.6055, 0.7703)\n centers[23] = (0.5758, 0.9026)\n centers[24] = (0.7622, 0.0593)\n centers[25] = (0.7300, 0.2172)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.8403523400947381
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_12/results/packing_viz.png",
+ "execution_time_mean": 23.97458342416212,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..53cfe58c386a5619db0bf19b97112fdb49cdcdaa
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..71fcc5af7c2b827c0fb87f0ab6e8e60f96f45862
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_121/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_121/results
+Run 1/1 completed in 293.23 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_121/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_121/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_121/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_121/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.5460320380265964
+ public: {'centers_str': ' centers[0] = (0.3029, 0.0862)\n centers[1] = (0.0989, 0.3171)\n centers[2] = (0.4512, 0.6254)\n centers[3] = (0.1220, 0.5367)\n centers[4] = (0.0703, 0.7218)\n centers[5] = (0.1057, 0.8943)\n centers[6] = (0.1091, 0.1091)\n centers[7] = (0.2399, 0.2281)\n centers[8] = (0.2816, 0.3873)\n centers[9] = (0.3269, 0.5619)\n centers[10] = (0.2473, 0.7352)\n centers[11] = (0.2921, 0.9185)\n centers[12] = (0.4690, 0.0800)\n centers[13] = (0.3980, 0.2350)\n centers[14] = (0.7441, 0.4805)\n centers[15] = (0.6464, 0.6722)\n centers[16] = (0.4452, 0.7759)\n centers[17] = (0.6026, 0.8964)\n centers[18] = (0.6961, 0.3112)\n centers[19] = (0.6644, 0.1195)\n centers[20] = (0.5170, 0.4368)\n centers[21] = (0.8944, 0.6017)\n centers[22] = (0.8519, 0.8519)\n centers[23] = (0.4375, 0.9351)\n centers[24] = (0.8917, 0.1089)\n centers[25] = (0.8848, 0.3346)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.5460320380265964}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_121/results/packing_viz.png
+ execution_time_mean: 293.2340498091653
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..a199f488af684ebceb2d47d32d96a0b5a0084761
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_121/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.5460320380265964,
+ "public": {
+ "centers_str": " centers[0] = (0.3029, 0.0862)\n centers[1] = (0.0989, 0.3171)\n centers[2] = (0.4512, 0.6254)\n centers[3] = (0.1220, 0.5367)\n centers[4] = (0.0703, 0.7218)\n centers[5] = (0.1057, 0.8943)\n centers[6] = (0.1091, 0.1091)\n centers[7] = (0.2399, 0.2281)\n centers[8] = (0.2816, 0.3873)\n centers[9] = (0.3269, 0.5619)\n centers[10] = (0.2473, 0.7352)\n centers[11] = (0.2921, 0.9185)\n centers[12] = (0.4690, 0.0800)\n centers[13] = (0.3980, 0.2350)\n centers[14] = (0.7441, 0.4805)\n centers[15] = (0.6464, 0.6722)\n centers[16] = (0.4452, 0.7759)\n centers[17] = (0.6026, 0.8964)\n centers[18] = (0.6961, 0.3112)\n centers[19] = (0.6644, 0.1195)\n centers[20] = (0.5170, 0.4368)\n centers[21] = (0.8944, 0.6017)\n centers[22] = (0.8519, 0.8519)\n centers[23] = (0.4375, 0.9351)\n centers[24] = (0.8917, 0.1089)\n centers[25] = (0.8848, 0.3346)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.5460320380265964
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_121/results/packing_viz.png",
+ "execution_time_mean": 293.2340498091653,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_122/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_122/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d96f9c1477b1d00a7c270b9de6bcff9fdc2ec4b8
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_122/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..63a4d66fb24bae032393dc04555491c95ba6c3aa
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..1bc06c76f6d9da6201a17bb6e9e55557a2d4cbd3
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_154/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_154/results
+Run 1/1 completed in 465.74 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_154/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_154/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_154/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_154/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.5450084676315545
+ public: {'centers_str': ' centers[0] = (0.0856, 0.0810)\n centers[1] = (0.0646, 0.2250)\n centers[2] = (0.1192, 0.3985)\n centers[3] = (0.0862, 0.5992)\n centers[4] = (0.0632, 0.7467)\n centers[5] = (0.0973, 0.9031)\n centers[6] = (0.4096, 0.2784)\n centers[7] = (0.2572, 0.0402)\n centers[8] = (0.3182, 0.3880)\n centers[9] = (0.2720, 0.5664)\n centers[10] = (0.2205, 0.7566)\n centers[11] = (0.2717, 0.9217)\n centers[12] = (0.6943, 0.1181)\n centers[13] = (0.2474, 0.1996)\n centers[14] = (0.5740, 0.3072)\n centers[15] = (0.4278, 0.7231)\n centers[16] = (0.6469, 0.6767)\n centers[17] = (0.6177, 0.8916)\n centers[18] = (0.9056, 0.0944)\n centers[19] = (0.4630, 0.1129)\n centers[20] = (0.4760, 0.5013)\n centers[21] = (0.8622, 0.8622)\n centers[22] = (0.8704, 0.5949)\n centers[23] = (0.4307, 0.9193)\n centers[24] = (0.6802, 0.4768)\n centers[25] = (0.8594, 0.3249)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.5450084676315545}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_154/results/packing_viz.png
+ execution_time_mean: 465.7389547410421
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..85cdbfe8f62213fac7481d8d9bc6e01f89c9b3f9
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_154/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.5450084676315545,
+ "public": {
+ "centers_str": " centers[0] = (0.0856, 0.0810)\n centers[1] = (0.0646, 0.2250)\n centers[2] = (0.1192, 0.3985)\n centers[3] = (0.0862, 0.5992)\n centers[4] = (0.0632, 0.7467)\n centers[5] = (0.0973, 0.9031)\n centers[6] = (0.4096, 0.2784)\n centers[7] = (0.2572, 0.0402)\n centers[8] = (0.3182, 0.3880)\n centers[9] = (0.2720, 0.5664)\n centers[10] = (0.2205, 0.7566)\n centers[11] = (0.2717, 0.9217)\n centers[12] = (0.6943, 0.1181)\n centers[13] = (0.2474, 0.1996)\n centers[14] = (0.5740, 0.3072)\n centers[15] = (0.4278, 0.7231)\n centers[16] = (0.6469, 0.6767)\n centers[17] = (0.6177, 0.8916)\n centers[18] = (0.9056, 0.0944)\n centers[19] = (0.4630, 0.1129)\n centers[20] = (0.4760, 0.5013)\n centers[21] = (0.8622, 0.8622)\n centers[22] = (0.8704, 0.5949)\n centers[23] = (0.4307, 0.9193)\n centers[24] = (0.6802, 0.4768)\n centers[25] = (0.8594, 0.3249)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.5450084676315545
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_154/results/packing_viz.png",
+ "execution_time_mean": 465.7389547410421,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..496622dd13ce662d0bd5ba6086e3194e8407e951
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..59dc064f2eb498ba122b51ccdb006a0664afd336
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_155/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_155/results
+Run 1/1 completed in 732.93 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_155/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_155/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_155/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_155/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.5794489231497497
+ public: {'centers_str': ' centers[0] = (0.2684, 0.5226)\n centers[1] = (0.0623, 0.0623)\n centers[2] = (0.0998, 0.3794)\n centers[3] = (0.5195, 0.9077)\n centers[4] = (0.1059, 0.6987)\n centers[5] = (0.3491, 0.9210)\n centers[6] = (0.0854, 0.2082)\n centers[7] = (0.2341, 0.8226)\n centers[8] = (0.6896, 0.4772)\n centers[9] = (0.8796, 0.6089)\n centers[10] = (0.3990, 0.7339)\n centers[11] = (0.2200, 0.9472)\n centers[12] = (0.2105, 0.0881)\n centers[13] = (0.2690, 0.2782)\n centers[14] = (0.7152, 0.2848)\n centers[15] = (0.4960, 0.5475)\n centers[16] = (0.0889, 0.8932)\n centers[17] = (0.6841, 0.9266)\n centers[18] = (0.4051, 0.1075)\n centers[19] = (0.5054, 0.3225)\n centers[20] = (0.8880, 0.3760)\n centers[21] = (0.6491, 0.7204)\n centers[22] = (0.8728, 0.8584)\n centers[23] = (0.0674, 0.5297)\n centers[24] = (0.8678, 0.1324)\n centers[25] = (0.6245, 0.1119)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.5794489231497497}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_155/results/packing_viz.png
+ execution_time_mean: 732.9288596659899
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..342f718ae25403a48aaf6d067d750ecbf10a389a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_155/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.5794489231497497,
+ "public": {
+ "centers_str": " centers[0] = (0.2684, 0.5226)\n centers[1] = (0.0623, 0.0623)\n centers[2] = (0.0998, 0.3794)\n centers[3] = (0.5195, 0.9077)\n centers[4] = (0.1059, 0.6987)\n centers[5] = (0.3491, 0.9210)\n centers[6] = (0.0854, 0.2082)\n centers[7] = (0.2341, 0.8226)\n centers[8] = (0.6896, 0.4772)\n centers[9] = (0.8796, 0.6089)\n centers[10] = (0.3990, 0.7339)\n centers[11] = (0.2200, 0.9472)\n centers[12] = (0.2105, 0.0881)\n centers[13] = (0.2690, 0.2782)\n centers[14] = (0.7152, 0.2848)\n centers[15] = (0.4960, 0.5475)\n centers[16] = (0.0889, 0.8932)\n centers[17] = (0.6841, 0.9266)\n centers[18] = (0.4051, 0.1075)\n centers[19] = (0.5054, 0.3225)\n centers[20] = (0.8880, 0.3760)\n centers[21] = (0.6491, 0.7204)\n centers[22] = (0.8728, 0.8584)\n centers[23] = (0.0674, 0.5297)\n centers[24] = (0.8678, 0.1324)\n centers[25] = (0.6245, 0.1119)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.5794489231497497
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_155/results/packing_viz.png",
+ "execution_time_mean": 732.9288596659899,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f4929ef0aee4b2446bc198a771bd26ce8c2041db
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..674cf47d22b21f485ca825e549d04c970e6f4312
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_185/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_185/results
+Run 1/1 completed in 731.95 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_185/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_185/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_185/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_185/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.60080527859162
+ public: {'centers_str': ' centers[0] = (0.2625, 0.0641)\n centers[1] = (0.4267, 0.1052)\n centers[2] = (0.2795, 0.4214)\n centers[3] = (0.1025, 0.7363)\n centers[4] = (0.4699, 0.9301)\n centers[5] = (0.2828, 0.8749)\n centers[6] = (0.4350, 0.3026)\n centers[7] = (0.2649, 0.2234)\n centers[8] = (0.8910, 0.6413)\n centers[9] = (0.1025, 0.1105)\n centers[10] = (0.6820, 0.6855)\n centers[11] = (0.0812, 0.9188)\n centers[12] = (0.1036, 0.5303)\n centers[13] = (0.0997, 0.3270)\n centers[14] = (0.2937, 0.6372)\n centers[15] = (0.5066, 0.5153)\n centers[16] = (0.6227, 0.3191)\n centers[17] = (0.4809, 0.7532)\n centers[18] = (0.8081, 0.0601)\n centers[19] = (0.9341, 0.0659)\n centers[20] = (0.7442, 0.4824)\n centers[21] = (0.8738, 0.8747)\n centers[22] = (0.9256, 0.4614)\n centers[23] = (0.6426, 0.8933)\n centers[24] = (0.6439, 0.1121)\n centers[25] = (0.8527, 0.2574)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.60080527859162}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_185/results/packing_viz.png
+ execution_time_mean: 731.9516496369615
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..0daf13d1928623bb08e4a635888657f62e61818f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_185/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.60080527859162,
+ "public": {
+ "centers_str": " centers[0] = (0.2625, 0.0641)\n centers[1] = (0.4267, 0.1052)\n centers[2] = (0.2795, 0.4214)\n centers[3] = (0.1025, 0.7363)\n centers[4] = (0.4699, 0.9301)\n centers[5] = (0.2828, 0.8749)\n centers[6] = (0.4350, 0.3026)\n centers[7] = (0.2649, 0.2234)\n centers[8] = (0.8910, 0.6413)\n centers[9] = (0.1025, 0.1105)\n centers[10] = (0.6820, 0.6855)\n centers[11] = (0.0812, 0.9188)\n centers[12] = (0.1036, 0.5303)\n centers[13] = (0.0997, 0.3270)\n centers[14] = (0.2937, 0.6372)\n centers[15] = (0.5066, 0.5153)\n centers[16] = (0.6227, 0.3191)\n centers[17] = (0.4809, 0.7532)\n centers[18] = (0.8081, 0.0601)\n centers[19] = (0.9341, 0.0659)\n centers[20] = (0.7442, 0.4824)\n centers[21] = (0.8738, 0.8747)\n centers[22] = (0.9256, 0.4614)\n centers[23] = (0.6426, 0.8933)\n centers[24] = (0.6439, 0.1121)\n centers[25] = (0.8527, 0.2574)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.60080527859162
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_185/results/packing_viz.png",
+ "execution_time_mean": 731.9516496369615,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f7ca81f296fb834e9fa50ca29c1eb028dac4f95e
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..2f4b76e676a3640d59143b5373f104a4f083e222
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_187/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_187/results
+Run 1/1 completed in 653.84 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_187/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_187/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_187/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_187/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.2968407881890545
+ public: {'centers_str': ' centers[0] = (0.0925, 0.0813)\n centers[1] = (0.0953, 0.2566)\n centers[2] = (0.0698, 0.4327)\n centers[3] = (0.0920, 0.5929)\n centers[4] = (0.0839, 0.7684)\n centers[5] = (0.0711, 0.9299)\n centers[6] = (0.2548, 0.0941)\n centers[7] = (0.7764, 0.0831)\n centers[8] = (0.2518, 0.4021)\n centers[9] = (0.9098, 0.1823)\n centers[10] = (0.2586, 0.7452)\n centers[11] = (0.2223, 0.9076)\n centers[12] = (0.7318, 0.9184)\n centers[13] = (0.3806, 0.1924)\n centers[14] = (0.4373, 0.3657)\n centers[15] = (0.4248, 0.5989)\n centers[16] = (0.8577, 0.3798)\n centers[17] = (0.4157, 0.9063)\n centers[18] = (0.8006, 0.6952)\n centers[19] = (0.9186, 0.9239)\n centers[20] = (0.5812, 0.4278)\n centers[21] = (0.2542, 0.5904)\n centers[22] = (0.5525, 0.7416)\n centers[23] = (0.5796, 0.9096)\n centers[24] = (0.5746, 0.1255)\n centers[25] = (0.7400, 0.2336)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.2968407881890545}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_187/results/packing_viz.png
+ execution_time_mean: 653.8384874211624
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..744b1a19debd870723742811debf7d23938b261e
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_187/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.2968407881890545,
+ "public": {
+ "centers_str": " centers[0] = (0.0925, 0.0813)\n centers[1] = (0.0953, 0.2566)\n centers[2] = (0.0698, 0.4327)\n centers[3] = (0.0920, 0.5929)\n centers[4] = (0.0839, 0.7684)\n centers[5] = (0.0711, 0.9299)\n centers[6] = (0.2548, 0.0941)\n centers[7] = (0.7764, 0.0831)\n centers[8] = (0.2518, 0.4021)\n centers[9] = (0.9098, 0.1823)\n centers[10] = (0.2586, 0.7452)\n centers[11] = (0.2223, 0.9076)\n centers[12] = (0.7318, 0.9184)\n centers[13] = (0.3806, 0.1924)\n centers[14] = (0.4373, 0.3657)\n centers[15] = (0.4248, 0.5989)\n centers[16] = (0.8577, 0.3798)\n centers[17] = (0.4157, 0.9063)\n centers[18] = (0.8006, 0.6952)\n centers[19] = (0.9186, 0.9239)\n centers[20] = (0.5812, 0.4278)\n centers[21] = (0.2542, 0.5904)\n centers[22] = (0.5525, 0.7416)\n centers[23] = (0.5796, 0.9096)\n centers[24] = (0.5746, 0.1255)\n centers[25] = (0.7400, 0.2336)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.2968407881890545
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_187/results/packing_viz.png",
+ "execution_time_mean": 653.8384874211624,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d4de2064d4f9c9c0a480e95a3cf22a1c0e5858e9
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..2ab68dcce4b29582aab18a1599622ba38d906a6a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_189/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_189/results
+Run 1/1 completed in 726.24 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_189/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_189/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_189/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_189/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.274372763295705
+ public: {'centers_str': ' centers[0] = (0.0925, 0.0813)\n centers[1] = (0.0953, 0.2566)\n centers[2] = (0.0698, 0.4325)\n centers[3] = (0.0920, 0.5929)\n centers[4] = (0.9251, 0.0773)\n centers[5] = (0.1370, 0.9205)\n centers[6] = (0.2548, 0.0941)\n centers[7] = (0.7780, 0.6949)\n centers[8] = (0.8938, 0.5203)\n centers[9] = (0.2134, 0.3686)\n centers[10] = (0.2586, 0.7452)\n centers[11] = (0.0691, 0.7883)\n centers[12] = (0.4259, 0.0732)\n centers[13] = (0.4156, 0.2348)\n centers[14] = (0.4051, 0.4228)\n centers[15] = (0.4248, 0.5989)\n centers[16] = (0.8980, 0.8620)\n centers[17] = (0.4157, 0.9063)\n centers[18] = (0.5710, 0.0825)\n centers[19] = (0.2566, 0.5269)\n centers[20] = (0.5812, 0.4278)\n centers[21] = (0.7384, 0.4914)\n centers[22] = (0.5944, 0.7602)\n centers[23] = (0.6142, 0.5918)\n centers[24] = (0.7315, 0.9090)\n centers[25] = (0.7400, 0.2336)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.274372763295705}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_189/results/packing_viz.png
+ execution_time_mean: 726.238574472256
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..4af5ecf33c4496fe45b9bc4833be3b7e927f74c5
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_189/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.274372763295705,
+ "public": {
+ "centers_str": " centers[0] = (0.0925, 0.0813)\n centers[1] = (0.0953, 0.2566)\n centers[2] = (0.0698, 0.4325)\n centers[3] = (0.0920, 0.5929)\n centers[4] = (0.9251, 0.0773)\n centers[5] = (0.1370, 0.9205)\n centers[6] = (0.2548, 0.0941)\n centers[7] = (0.7780, 0.6949)\n centers[8] = (0.8938, 0.5203)\n centers[9] = (0.2134, 0.3686)\n centers[10] = (0.2586, 0.7452)\n centers[11] = (0.0691, 0.7883)\n centers[12] = (0.4259, 0.0732)\n centers[13] = (0.4156, 0.2348)\n centers[14] = (0.4051, 0.4228)\n centers[15] = (0.4248, 0.5989)\n centers[16] = (0.8980, 0.8620)\n centers[17] = (0.4157, 0.9063)\n centers[18] = (0.5710, 0.0825)\n centers[19] = (0.2566, 0.5269)\n centers[20] = (0.5812, 0.4278)\n centers[21] = (0.7384, 0.4914)\n centers[22] = (0.5944, 0.7602)\n centers[23] = (0.6142, 0.5918)\n centers[24] = (0.7315, 0.9090)\n centers[25] = (0.7400, 0.2336)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.274372763295705
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_189/results/packing_viz.png",
+ "execution_time_mean": 726.238574472256,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_190/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_190/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_190/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_190/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_190/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..64d8c0e83855a31fef82e4e78674df7c88461a19
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_190/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.60080527859162,
+ "public": {
+ "centers_str": " centers[0] = (0.2625, 0.0641)\n centers[1] = (0.4267, 0.1052)\n centers[2] = (0.2795, 0.4214)\n centers[3] = (0.1025, 0.7363)\n centers[4] = (0.4699, 0.9301)\n centers[5] = (0.2828, 0.8749)\n centers[6] = (0.4350, 0.3026)\n centers[7] = (0.2649, 0.2234)\n centers[8] = (0.8910, 0.6413)\n centers[9] = (0.1025, 0.1105)\n centers[10] = (0.6820, 0.6855)\n centers[11] = (0.0812, 0.9188)\n centers[12] = (0.1036, 0.5303)\n centers[13] = (0.0997, 0.3270)\n centers[14] = (0.2937, 0.6372)\n centers[15] = (0.5066, 0.5153)\n centers[16] = (0.6227, 0.3191)\n centers[17] = (0.4809, 0.7532)\n centers[18] = (0.8081, 0.0601)\n centers[19] = (0.9341, 0.0659)\n centers[20] = (0.7442, 0.4824)\n centers[21] = (0.8738, 0.8747)\n centers[22] = (0.9256, 0.4614)\n centers[23] = (0.6426, 0.8933)\n centers[24] = (0.6439, 0.1121)\n centers[25] = (0.8527, 0.2574)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.60080527859162
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_190/results/packing_viz.png",
+ "execution_time_mean": 696.9400353301316,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b645643ccde33a453f850b7cf8404ab34cb965cf
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..17235cfc3400d7d7c108803d0ef3c8702b9403da
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_198/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_198/results
+Run 1/1 completed in 684.21 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_198/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_198/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_198/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_198/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.60080527859162
+ public: {'centers_str': ' centers[0] = (0.2625, 0.0641)\n centers[1] = (0.4267, 0.1052)\n centers[2] = (0.2795, 0.4214)\n centers[3] = (0.1025, 0.7363)\n centers[4] = (0.4699, 0.9301)\n centers[5] = (0.2828, 0.8749)\n centers[6] = (0.4350, 0.3026)\n centers[7] = (0.2649, 0.2234)\n centers[8] = (0.8910, 0.6413)\n centers[9] = (0.1025, 0.1105)\n centers[10] = (0.6820, 0.6855)\n centers[11] = (0.0812, 0.9188)\n centers[12] = (0.1036, 0.5303)\n centers[13] = (0.0997, 0.3270)\n centers[14] = (0.2937, 0.6372)\n centers[15] = (0.5066, 0.5153)\n centers[16] = (0.6227, 0.3191)\n centers[17] = (0.4809, 0.7532)\n centers[18] = (0.8081, 0.0601)\n centers[19] = (0.9341, 0.0659)\n centers[20] = (0.7442, 0.4824)\n centers[21] = (0.8738, 0.8747)\n centers[22] = (0.9256, 0.4614)\n centers[23] = (0.6426, 0.8933)\n centers[24] = (0.6439, 0.1121)\n centers[25] = (0.8527, 0.2574)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.60080527859162}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_198/results/packing_viz.png
+ execution_time_mean: 684.2101724930108
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..5e089a5cc9a0857c680f52ad3096650419a3a458
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_198/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.60080527859162,
+ "public": {
+ "centers_str": " centers[0] = (0.2625, 0.0641)\n centers[1] = (0.4267, 0.1052)\n centers[2] = (0.2795, 0.4214)\n centers[3] = (0.1025, 0.7363)\n centers[4] = (0.4699, 0.9301)\n centers[5] = (0.2828, 0.8749)\n centers[6] = (0.4350, 0.3026)\n centers[7] = (0.2649, 0.2234)\n centers[8] = (0.8910, 0.6413)\n centers[9] = (0.1025, 0.1105)\n centers[10] = (0.6820, 0.6855)\n centers[11] = (0.0812, 0.9188)\n centers[12] = (0.1036, 0.5303)\n centers[13] = (0.0997, 0.3270)\n centers[14] = (0.2937, 0.6372)\n centers[15] = (0.5066, 0.5153)\n centers[16] = (0.6227, 0.3191)\n centers[17] = (0.4809, 0.7532)\n centers[18] = (0.8081, 0.0601)\n centers[19] = (0.9341, 0.0659)\n centers[20] = (0.7442, 0.4824)\n centers[21] = (0.8738, 0.8747)\n centers[22] = (0.9256, 0.4614)\n centers[23] = (0.6426, 0.8933)\n centers[24] = (0.6439, 0.1121)\n centers[25] = (0.8527, 0.2574)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.60080527859162
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_198/results/packing_viz.png",
+ "execution_time_mean": 684.2101724930108,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_199/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_199/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9f69f850f7f1f239527c5915dfe2cf1455e933f1
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_199/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_199/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_199/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..1859ee02ff4456521fc64cae53bd97d8dba63884
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_199/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.5326883293702784,
+ "public": {
+ "centers_str": " centers[0] = (0.7807, 0.2725)\n centers[1] = (0.0603, 0.0603)\n centers[2] = (0.0888, 0.3930)\n centers[3] = (0.0936, 0.2106)\n centers[4] = (0.1140, 0.5943)\n centers[5] = (0.1469, 0.8531)\n centers[6] = (0.1954, 0.0755)\n centers[7] = (0.9311, 0.0689)\n centers[8] = (0.7581, 0.9040)\n centers[9] = (0.6056, 0.0639)\n centers[10] = (0.2546, 0.4511)\n centers[11] = (0.3797, 0.9078)\n centers[12] = (0.7653, 0.1001)\n centers[13] = (0.3239, 0.0547)\n centers[14] = (0.4199, 0.4257)\n centers[15] = (0.4592, 0.0845)\n centers[16] = (0.5779, 0.5119)\n centers[17] = (0.3801, 0.6592)\n centers[18] = (0.3150, 0.2428)\n centers[19] = (0.9081, 0.2281)\n centers[20] = (0.6598, 0.7086)\n centers[21] = (0.5667, 0.9052)\n centers[22] = (0.8904, 0.7466)\n centers[23] = (0.9263, 0.9263)\n centers[24] = (0.5905, 0.2748)\n centers[25] = (0.8329, 0.4760)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.5326883293702784
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_199/results/packing_viz.png",
+ "execution_time_mean": 863.7634269343689,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1a31742df179c7fdf0306c7d881b390497c89648
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..a4f993fef030af7f6eb2e1e218ac5cbd9f53c8b1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_23/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_23/results
+Run 1/1 completed in 23.34 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_23/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_23/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_23/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_23/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.8403523400947381
+ public: {'centers_str': ' centers[0] = (0.1016, 0.0793)\n centers[1] = (0.1072, 0.2632)\n centers[2] = (0.0563, 0.4484)\n centers[3] = (0.1007, 0.6024)\n centers[4] = (0.0585, 0.7467)\n centers[5] = (0.0747, 0.9451)\n centers[6] = (0.2596, 0.1049)\n centers[7] = (0.2462, 0.2318)\n centers[8] = (0.2536, 0.3876)\n centers[9] = (0.2718, 0.5921)\n centers[10] = (0.2672, 0.7403)\n centers[11] = (0.2814, 0.9429)\n centers[12] = (0.4352, 0.0630)\n centers[13] = (0.4144, 0.2196)\n centers[14] = (0.3936, 0.4289)\n centers[15] = (0.4330, 0.6145)\n centers[16] = (0.4051, 0.7414)\n centers[17] = (0.4146, 0.8960)\n centers[18] = (0.5587, 0.0817)\n centers[19] = (0.5651, 0.2613)\n centers[20] = (0.5791, 0.4388)\n centers[21] = (0.5967, 0.5708)\n centers[22] = (0.6055, 0.7703)\n centers[23] = (0.5758, 0.9026)\n centers[24] = (0.7622, 0.0593)\n centers[25] = (0.7300, 0.2172)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.8403523400947381}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_23/results/packing_viz.png
+ execution_time_mean: 23.341649210080504
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..0e20f4420054e11a467d36644f2e4cd8ff8e9e3f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_23/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.8403523400947381,
+ "public": {
+ "centers_str": " centers[0] = (0.1016, 0.0793)\n centers[1] = (0.1072, 0.2632)\n centers[2] = (0.0563, 0.4484)\n centers[3] = (0.1007, 0.6024)\n centers[4] = (0.0585, 0.7467)\n centers[5] = (0.0747, 0.9451)\n centers[6] = (0.2596, 0.1049)\n centers[7] = (0.2462, 0.2318)\n centers[8] = (0.2536, 0.3876)\n centers[9] = (0.2718, 0.5921)\n centers[10] = (0.2672, 0.7403)\n centers[11] = (0.2814, 0.9429)\n centers[12] = (0.4352, 0.0630)\n centers[13] = (0.4144, 0.2196)\n centers[14] = (0.3936, 0.4289)\n centers[15] = (0.4330, 0.6145)\n centers[16] = (0.4051, 0.7414)\n centers[17] = (0.4146, 0.8960)\n centers[18] = (0.5587, 0.0817)\n centers[19] = (0.5651, 0.2613)\n centers[20] = (0.5791, 0.4388)\n centers[21] = (0.5967, 0.5708)\n centers[22] = (0.6055, 0.7703)\n centers[23] = (0.5758, 0.9026)\n centers[24] = (0.7622, 0.0593)\n centers[25] = (0.7300, 0.2172)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.8403523400947381
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_23/results/packing_viz.png",
+ "execution_time_mean": 23.341649210080504,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_26/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_26/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_26/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_26/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_26/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_26/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_26/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_26/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..2deb555e3fd8b39c69bb42b94822629a3a8f23df
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_26/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9128786163575386,
+ "public": {
+ "centers_str": " centers[0] = (0.0970, 0.0803)\n centers[1] = (0.1013, 0.2599)\n centers[2] = (0.0630, 0.4404)\n centers[3] = (0.0964, 0.5976)\n centers[4] = (0.0647, 0.7475)\n centers[5] = (0.0769, 0.9380)\n centers[6] = (0.2572, 0.0995)\n centers[7] = (0.2472, 0.2364)\n centers[8] = (0.2527, 0.3949)\n centers[9] = (0.2664, 0.5899)\n centers[10] = (0.2629, 0.7427)\n centers[11] = (0.2735, 0.9363)\n centers[12] = (0.4306, 0.0681)\n centers[13] = (0.4150, 0.2272)\n centers[14] = (0.3994, 0.4258)\n centers[15] = (0.4289, 0.6067)\n centers[16] = (0.4080, 0.7435)\n centers[17] = (0.4151, 0.9011)\n centers[18] = (0.5648, 0.0821)\n centers[19] = (0.5697, 0.2585)\n centers[20] = (0.5802, 0.4333)\n centers[21] = (0.5933, 0.5740)\n centers[22] = (0.5999, 0.7652)\n centers[23] = (0.5777, 0.9061)\n centers[24] = (0.7591, 0.0653)\n centers[25] = (0.7350, 0.2254)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9128786163575386
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_26/results/packing_viz.png",
+ "execution_time_mean": 26.099498846102506,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d88509397dae4c6fb62f50acd201a915b387d434
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..7d718d5e9e88dc8eaa666e9b1b16cc95debb1eb0
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_27/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_27/results
+Run 1/1 completed in 44.06 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_27/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_27/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_27/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_27/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.9785243758578348
+ public: {'centers_str': ' centers[0] = (0.0885, 0.0527)\n centers[1] = (0.0747, 0.3229)\n centers[2] = (0.0416, 0.4897)\n centers[3] = (0.0752, 0.6029)\n centers[4] = (0.0432, 0.7470)\n centers[5] = (0.0744, 0.9450)\n centers[6] = (0.2361, 0.0744)\n centers[7] = (0.1360, 0.1691)\n centers[8] = (0.1774, 0.3661)\n centers[9] = (0.2759, 0.5993)\n centers[10] = (0.1906, 0.8121)\n centers[11] = (0.2053, 0.9594)\n centers[12] = (0.2206, 0.0326)\n centers[13] = (0.3881, 0.1844)\n centers[14] = (0.4087, 0.4102)\n centers[15] = (0.5065, 0.6006)\n centers[16] = (0.4103, 0.7923)\n centers[17] = (0.4014, 0.9506)\n centers[18] = (0.5625, 0.0604)\n centers[19] = (0.6081, 0.2166)\n centers[20] = (0.6202, 0.4208)\n centers[21] = (0.6663, 0.5746)\n centers[22] = (0.7101, 0.8153)\n centers[23] = (0.5537, 0.9688)\n centers[24] = (0.7968, 0.1356)\n centers[25] = (0.8815, 0.3002)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.9785243758578348}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_27/results/packing_viz.png
+ execution_time_mean: 44.05896951397881
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..962200ae64f84414db1769cfa3ea3ad838fb768f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_27/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.9785243758578348,
+ "public": {
+ "centers_str": " centers[0] = (0.0885, 0.0527)\n centers[1] = (0.0747, 0.3229)\n centers[2] = (0.0416, 0.4897)\n centers[3] = (0.0752, 0.6029)\n centers[4] = (0.0432, 0.7470)\n centers[5] = (0.0744, 0.9450)\n centers[6] = (0.2361, 0.0744)\n centers[7] = (0.1360, 0.1691)\n centers[8] = (0.1774, 0.3661)\n centers[9] = (0.2759, 0.5993)\n centers[10] = (0.1906, 0.8121)\n centers[11] = (0.2053, 0.9594)\n centers[12] = (0.2206, 0.0326)\n centers[13] = (0.3881, 0.1844)\n centers[14] = (0.4087, 0.4102)\n centers[15] = (0.5065, 0.6006)\n centers[16] = (0.4103, 0.7923)\n centers[17] = (0.4014, 0.9506)\n centers[18] = (0.5625, 0.0604)\n centers[19] = (0.6081, 0.2166)\n centers[20] = (0.6202, 0.4208)\n centers[21] = (0.6663, 0.5746)\n centers[22] = (0.7101, 0.8153)\n centers[23] = (0.5537, 0.9688)\n centers[24] = (0.7968, 0.1356)\n centers[25] = (0.8815, 0.3002)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.9785243758578348
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_27/results/packing_viz.png",
+ "execution_time_mean": 44.05896951397881,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..124bd2656b23fd7bf8988188c78c9f3fbf38f236
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..25eb12fcaed441ed73b8ba36077e437af63d3285
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_41/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_41/results
+Run 1/1 completed in 533.50 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_41/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_41/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_41/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_41/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.4537632790006625
+ public: {'centers_str': ' centers[0] = (0.0694, 0.0694)\n centers[1] = (0.0977, 0.2340)\n centers[2] = (0.1254, 0.4514)\n centers[3] = (0.0578, 0.6175)\n centers[4] = (0.0550, 0.7302)\n centers[5] = (0.1125, 0.8875)\n centers[6] = (0.4053, 0.0788)\n centers[7] = (0.2320, 0.0953)\n centers[8] = (0.3017, 0.2936)\n centers[9] = (0.3467, 0.5112)\n centers[10] = (0.2126, 0.6820)\n centers[11] = (0.3511, 0.8735)\n centers[12] = (0.4677, 0.2133)\n centers[13] = (0.4947, 0.3747)\n centers[14] = (0.6724, 0.3829)\n centers[15] = (0.5881, 0.5781)\n centers[16] = (0.5020, 0.7572)\n centers[17] = (0.5733, 0.9024)\n centers[18] = (0.5764, 0.0928)\n centers[19] = (0.5450, 0.2113)\n centers[20] = (0.8556, 0.5176)\n centers[21] = (0.3918, 0.6820)\n centers[22] = (0.6195, 0.7585)\n centers[23] = (0.8305, 0.8305)\n centers[24] = (0.8276, 0.1777)\n centers[25] = (0.5993, 0.2514)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.4537632790006625}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_41/results/packing_viz.png
+ execution_time_mean: 533.5027654320002
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..e554b38c3248242b8dcb2c30f28bc9e81905adc2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_41/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.4537632790006625,
+ "public": {
+ "centers_str": " centers[0] = (0.0694, 0.0694)\n centers[1] = (0.0977, 0.2340)\n centers[2] = (0.1254, 0.4514)\n centers[3] = (0.0578, 0.6175)\n centers[4] = (0.0550, 0.7302)\n centers[5] = (0.1125, 0.8875)\n centers[6] = (0.4053, 0.0788)\n centers[7] = (0.2320, 0.0953)\n centers[8] = (0.3017, 0.2936)\n centers[9] = (0.3467, 0.5112)\n centers[10] = (0.2126, 0.6820)\n centers[11] = (0.3511, 0.8735)\n centers[12] = (0.4677, 0.2133)\n centers[13] = (0.4947, 0.3747)\n centers[14] = (0.6724, 0.3829)\n centers[15] = (0.5881, 0.5781)\n centers[16] = (0.5020, 0.7572)\n centers[17] = (0.5733, 0.9024)\n centers[18] = (0.5764, 0.0928)\n centers[19] = (0.5450, 0.2113)\n centers[20] = (0.8556, 0.5176)\n centers[21] = (0.3918, 0.6820)\n centers[22] = (0.6195, 0.7585)\n centers[23] = (0.8305, 0.8305)\n centers[24] = (0.8276, 0.1777)\n centers[25] = (0.5993, 0.2514)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.4537632790006625
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_41/results/packing_viz.png",
+ "execution_time_mean": 533.5027654320002,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ca76f39fa0a965fa583586d8d3386cb13279fe8e
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..9c4eacf50fc40547ba81566c49580cab863f9e75
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_44/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_44/results
+Run 1/1 completed in 52.53 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_44/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_44/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_44/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_44/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.143133448331026
+ public: {'centers_str': ' centers[0] = (0.0919, 0.0767)\n centers[1] = (0.0696, 0.2621)\n centers[2] = (0.0517, 0.4501)\n centers[3] = (0.0822, 0.6012)\n centers[4] = (0.0079, 0.8109)\n centers[5] = (0.0747, 0.9451)\n centers[6] = (0.2713, 0.0633)\n centers[7] = (0.2159, 0.2036)\n centers[8] = (0.1873, 0.3847)\n centers[9] = (0.2625, 0.5665)\n centers[10] = (0.2027, 0.7926)\n centers[11] = (0.2244, 0.9622)\n centers[12] = (0.4269, 0.0472)\n centers[13] = (0.4064, 0.1982)\n centers[14] = (0.3912, 0.4072)\n centers[15] = (0.4484, 0.5918)\n centers[16] = (0.4268, 0.7641)\n centers[17] = (0.4030, 0.9255)\n centers[18] = (0.5688, 0.0834)\n centers[19] = (0.5719, 0.2566)\n centers[20] = (0.6170, 0.4455)\n centers[21] = (0.5659, 0.5878)\n centers[22] = (0.7444, 0.7753)\n centers[23] = (0.5268, 0.9510)\n centers[24] = (0.7686, 0.0209)\n centers[25] = (0.8223, 0.2211)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.143133448331026}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_44/results/packing_viz.png
+ execution_time_mean: 52.530282833147794
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..8ca3fe687db3feac22b3682ac43f93de18c96c1b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_44/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.143133448331026,
+ "public": {
+ "centers_str": " centers[0] = (0.0919, 0.0767)\n centers[1] = (0.0696, 0.2621)\n centers[2] = (0.0517, 0.4501)\n centers[3] = (0.0822, 0.6012)\n centers[4] = (0.0079, 0.8109)\n centers[5] = (0.0747, 0.9451)\n centers[6] = (0.2713, 0.0633)\n centers[7] = (0.2159, 0.2036)\n centers[8] = (0.1873, 0.3847)\n centers[9] = (0.2625, 0.5665)\n centers[10] = (0.2027, 0.7926)\n centers[11] = (0.2244, 0.9622)\n centers[12] = (0.4269, 0.0472)\n centers[13] = (0.4064, 0.1982)\n centers[14] = (0.3912, 0.4072)\n centers[15] = (0.4484, 0.5918)\n centers[16] = (0.4268, 0.7641)\n centers[17] = (0.4030, 0.9255)\n centers[18] = (0.5688, 0.0834)\n centers[19] = (0.5719, 0.2566)\n centers[20] = (0.6170, 0.4455)\n centers[21] = (0.5659, 0.5878)\n centers[22] = (0.7444, 0.7753)\n centers[23] = (0.5268, 0.9510)\n centers[24] = (0.7686, 0.0209)\n centers[25] = (0.8223, 0.2211)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.143133448331026
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_44/results/packing_viz.png",
+ "execution_time_mean": 52.530282833147794,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5f997176f0cad194b60dc7cb311c62e241b805d1
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..654135f0a5e30a5e7d9484711062a5f9a939ad45
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_50/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_50/results
+Run 1/1 completed in 2310.19 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_50/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_50/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_50/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_50/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.274904878876268
+ public: {'centers_str': ' centers[0] = (0.0720, 0.0952)\n centers[1] = (0.0671, 0.2489)\n centers[2] = (0.0965, 0.4104)\n centers[3] = (0.0836, 0.5899)\n centers[4] = (0.0822, 0.7548)\n centers[5] = (0.0827, 0.9182)\n centers[6] = (0.2318, 0.0878)\n centers[7] = (0.2272, 0.2706)\n centers[8] = (0.2513, 0.4176)\n centers[9] = (0.2637, 0.5679)\n centers[10] = (0.2442, 0.7422)\n centers[11] = (0.2551, 0.9106)\n centers[12] = (0.4152, 0.0943)\n centers[13] = (0.4040, 0.2697)\n centers[14] = (0.3883, 0.4337)\n centers[15] = (0.4487, 0.5934)\n centers[16] = (0.4072, 0.7607)\n centers[17] = (0.4229, 0.9224)\n centers[18] = (0.5782, 0.0699)\n centers[19] = (0.5757, 0.2330)\n centers[20] = (0.5616, 0.4142)\n centers[21] = (0.6183, 0.5578)\n centers[22] = (0.6513, 0.7467)\n centers[23] = (0.5819, 0.9298)\n centers[24] = (0.7383, 0.0909)\n centers[25] = (0.8223, 0.3464)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.274904878876268}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_50/results/packing_viz.png
+ execution_time_mean: 2310.1934076901525
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..58e718a62c05dc6bf0525c7eb7d4d0f56f0c624d
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_50/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.274904878876268,
+ "public": {
+ "centers_str": " centers[0] = (0.0720, 0.0952)\n centers[1] = (0.0671, 0.2489)\n centers[2] = (0.0965, 0.4104)\n centers[3] = (0.0836, 0.5899)\n centers[4] = (0.0822, 0.7548)\n centers[5] = (0.0827, 0.9182)\n centers[6] = (0.2318, 0.0878)\n centers[7] = (0.2272, 0.2706)\n centers[8] = (0.2513, 0.4176)\n centers[9] = (0.2637, 0.5679)\n centers[10] = (0.2442, 0.7422)\n centers[11] = (0.2551, 0.9106)\n centers[12] = (0.4152, 0.0943)\n centers[13] = (0.4040, 0.2697)\n centers[14] = (0.3883, 0.4337)\n centers[15] = (0.4487, 0.5934)\n centers[16] = (0.4072, 0.7607)\n centers[17] = (0.4229, 0.9224)\n centers[18] = (0.5782, 0.0699)\n centers[19] = (0.5757, 0.2330)\n centers[20] = (0.5616, 0.4142)\n centers[21] = (0.6183, 0.5578)\n centers[22] = (0.6513, 0.7467)\n centers[23] = (0.5819, 0.9298)\n centers[24] = (0.7383, 0.0909)\n centers[25] = (0.8223, 0.3464)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.274904878876268
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_50/results/packing_viz.png",
+ "execution_time_mean": 2310.1934076901525,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e643e39c14e0d5a285a66f91d5874e22a092cf68
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..2cdd1c1c71e23da7e4ee908ba45df613b988d752
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_64/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_64/results
+Run 1/1 completed in 24.58 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_64/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_64/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_64/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_64/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.8172959108373126
+ public: {'centers_str': ' centers[0] = (0.1016, 0.0793)\n centers[1] = (0.1004, 0.2667)\n centers[2] = (0.0563, 0.4484)\n centers[3] = (0.1007, 0.6024)\n centers[4] = (0.0585, 0.7467)\n centers[5] = (0.0747, 0.9451)\n centers[6] = (0.2596, 0.1049)\n centers[7] = (0.1712, 0.2310)\n centers[8] = (0.2536, 0.3876)\n centers[9] = (0.2718, 0.5921)\n centers[10] = (0.2672, 0.7403)\n centers[11] = (0.2814, 0.9429)\n centers[12] = (0.4352, 0.0630)\n centers[13] = (0.4144, 0.2196)\n centers[14] = (0.3936, 0.4289)\n centers[15] = (0.4385, 0.6140)\n centers[16] = (0.4051, 0.7414)\n centers[17] = (0.4146, 0.8960)\n centers[18] = (0.5587, 0.0817)\n centers[19] = (0.5651, 0.2613)\n centers[20] = (0.5791, 0.4388)\n centers[21] = (0.5104, 0.5963)\n centers[22] = (0.6055, 0.7703)\n centers[23] = (0.5389, 0.9481)\n centers[24] = (0.7622, 0.0593)\n centers[25] = (0.7300, 0.2172)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.8172959108373126}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_64/results/packing_viz.png
+ execution_time_mean: 24.5839552632533
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..d00d9c7937959aeccfd192dd5f71ed76b27f6d4a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_64/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.8172959108373126,
+ "public": {
+ "centers_str": " centers[0] = (0.1016, 0.0793)\n centers[1] = (0.1004, 0.2667)\n centers[2] = (0.0563, 0.4484)\n centers[3] = (0.1007, 0.6024)\n centers[4] = (0.0585, 0.7467)\n centers[5] = (0.0747, 0.9451)\n centers[6] = (0.2596, 0.1049)\n centers[7] = (0.1712, 0.2310)\n centers[8] = (0.2536, 0.3876)\n centers[9] = (0.2718, 0.5921)\n centers[10] = (0.2672, 0.7403)\n centers[11] = (0.2814, 0.9429)\n centers[12] = (0.4352, 0.0630)\n centers[13] = (0.4144, 0.2196)\n centers[14] = (0.3936, 0.4289)\n centers[15] = (0.4385, 0.6140)\n centers[16] = (0.4051, 0.7414)\n centers[17] = (0.4146, 0.8960)\n centers[18] = (0.5587, 0.0817)\n centers[19] = (0.5651, 0.2613)\n centers[20] = (0.5791, 0.4388)\n centers[21] = (0.5104, 0.5963)\n centers[22] = (0.6055, 0.7703)\n centers[23] = (0.5389, 0.9481)\n centers[24] = (0.7622, 0.0593)\n centers[25] = (0.7300, 0.2172)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.8172959108373126
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_64/results/packing_viz.png",
+ "execution_time_mean": 24.5839552632533,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_81/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_81/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_81/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a2962f03ac9dd4923a03373bddcea3b2b584a547
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..04acc4187d4d567de8ee0c31dc4588954e8f0076
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_83/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_83/results
+Run 1/1 completed in 143.51 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_83/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_83/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_83/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_83/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 0.005798551035792316
+ public: {'centers_str': ' centers[0] = (0.5002, 0.5010)\n centers[1] = (0.5001, 0.5008)\n centers[2] = (0.5000, 0.4995)\n centers[3] = (0.4986, 0.4998)\n centers[4] = (0.5005, 0.5014)\n centers[5] = (0.4994, 0.4990)\n centers[6] = (0.4999, 0.5008)\n centers[7] = (0.5012, 0.4994)\n centers[8] = (0.4995, 0.4995)\n centers[9] = (0.5015, 0.4992)\n centers[10] = (0.5015, 0.4999)\n centers[11] = (0.4996, 0.4999)\n centers[12] = (0.5005, 0.4989)\n centers[13] = (0.5006, 0.5000)\n centers[14] = (0.5006, 0.4996)\n centers[15] = (0.4993, 0.5007)\n centers[16] = (0.4993, 0.5000)\n centers[17] = (0.5011, 0.4998)\n centers[18] = (0.4990, 0.5005)\n centers[19] = (0.5003, 0.5003)\n centers[20] = (0.5018, 0.5008)\n centers[21] = (0.5008, 0.5003)\n centers[22] = (0.4998, 0.4990)\n centers[23] = (0.5012, 0.5004)\n centers[24] = (0.4991, 0.4994)\n centers[25] = (0.5000, 0.5000)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 0.005798551035792316}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_83/results/packing_viz.png
+ execution_time_mean: 143.51382414903492
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..1b6c1d16bd5cf7a750cc0d340750bfd7909cbb11
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 0.005798551035792316,
+ "public": {
+ "centers_str": " centers[0] = (0.5002, 0.5010)\n centers[1] = (0.5001, 0.5008)\n centers[2] = (0.5000, 0.4995)\n centers[3] = (0.4986, 0.4998)\n centers[4] = (0.5005, 0.5014)\n centers[5] = (0.4994, 0.4990)\n centers[6] = (0.4999, 0.5008)\n centers[7] = (0.5012, 0.4994)\n centers[8] = (0.4995, 0.4995)\n centers[9] = (0.5015, 0.4992)\n centers[10] = (0.5015, 0.4999)\n centers[11] = (0.4996, 0.4999)\n centers[12] = (0.5005, 0.4989)\n centers[13] = (0.5006, 0.5000)\n centers[14] = (0.5006, 0.4996)\n centers[15] = (0.4993, 0.5007)\n centers[16] = (0.4993, 0.5000)\n centers[17] = (0.5011, 0.4998)\n centers[18] = (0.4990, 0.5005)\n centers[19] = (0.5003, 0.5003)\n centers[20] = (0.5018, 0.5008)\n centers[21] = (0.5008, 0.5003)\n centers[22] = (0.4998, 0.4990)\n centers[23] = (0.5012, 0.5004)\n centers[24] = (0.4991, 0.4994)\n centers[25] = (0.5000, 0.5000)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 0.005798551035792316
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_83/results/packing_viz.png",
+ "execution_time_mean": 143.51382414903492,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/packing_viz.png b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/packing_viz.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc3381aba7dde38aab2352ccabcbd30c6063182e
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_83/results/packing_viz.png differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..751bac63427580176f7f210fec39853b191c09cb
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..6726711eac092548fd40c98564caaebfdca16c21
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_90/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_90/results
+Run 1/1 completed in 246.69 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_90/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_90/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_90/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_90/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.5000589695108113
+ public: {'centers_str': ' centers[0] = (0.0681, 0.0681)\n centers[1] = (0.0846, 0.2199)\n centers[2] = (0.1070, 0.5454)\n centers[3] = (0.0694, 0.3731)\n centers[4] = (0.0996, 0.7523)\n centers[5] = (0.0735, 0.9264)\n centers[6] = (0.2384, 0.1067)\n centers[7] = (0.3930, 0.0561)\n centers[8] = (0.4663, 0.4103)\n centers[9] = (0.3211, 0.5565)\n centers[10] = (0.3865, 0.9186)\n centers[11] = (0.2266, 0.9192)\n centers[12] = (0.5273, 0.0805)\n centers[13] = (0.6674, 0.4591)\n centers[14] = (0.5193, 0.5985)\n centers[15] = (0.4058, 0.1936)\n centers[16] = (0.5404, 0.9261)\n centers[17] = (0.2947, 0.7577)\n centers[18] = (0.2578, 0.3352)\n centers[19] = (0.6975, 0.0947)\n centers[20] = (0.5733, 0.2493)\n centers[21] = (0.8877, 0.4535)\n centers[22] = (0.4773, 0.7769)\n centers[23] = (0.7831, 0.7656)\n centers[24] = (0.7690, 0.2742)\n centers[25] = (0.8937, 0.1065)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.5000589695108113}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_90/results/packing_viz.png
+ execution_time_mean: 246.69021986005828
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..148d49f6fd29373e33365b223c5759ae36618afb
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_90/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.5000589695108113,
+ "public": {
+ "centers_str": " centers[0] = (0.0681, 0.0681)\n centers[1] = (0.0846, 0.2199)\n centers[2] = (0.1070, 0.5454)\n centers[3] = (0.0694, 0.3731)\n centers[4] = (0.0996, 0.7523)\n centers[5] = (0.0735, 0.9264)\n centers[6] = (0.2384, 0.1067)\n centers[7] = (0.3930, 0.0561)\n centers[8] = (0.4663, 0.4103)\n centers[9] = (0.3211, 0.5565)\n centers[10] = (0.3865, 0.9186)\n centers[11] = (0.2266, 0.9192)\n centers[12] = (0.5273, 0.0805)\n centers[13] = (0.6674, 0.4591)\n centers[14] = (0.5193, 0.5985)\n centers[15] = (0.4058, 0.1936)\n centers[16] = (0.5404, 0.9261)\n centers[17] = (0.2947, 0.7577)\n centers[18] = (0.2578, 0.3352)\n centers[19] = (0.6975, 0.0947)\n centers[20] = (0.5733, 0.2493)\n centers[21] = (0.8877, 0.4535)\n centers[22] = (0.4773, 0.7769)\n centers[23] = (0.7831, 0.7656)\n centers[24] = (0.7690, 0.2742)\n centers[25] = (0.8937, 0.1065)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.5000589695108113
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_90/results/packing_viz.png",
+ "execution_time_mean": 246.69021986005828,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_91/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_91/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_91/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_91/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_91/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..874fdaa5a7b53ecf82fc881a0087b4abe2623e7a
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_91/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_91/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_91/results
+Run 1/1 completed in 390.17 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_91/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_91/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_91/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_91/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.3931247645645037
+ public: {'centers_str': ' centers[0] = (0.0000, 0.3445)\n centers[1] = (0.1473, 0.2934)\n centers[2] = (0.0580, 0.0602)\n centers[3] = (0.0000, 0.2801)\n centers[4] = (0.0000, 1.0000)\n centers[5] = (0.0685, 0.6459)\n centers[6] = (0.3206, 0.0000)\n centers[7] = (0.0000, 0.0000)\n centers[8] = (0.6862, 0.0000)\n centers[9] = (0.1397, 0.8604)\n centers[10] = (0.3234, 0.5321)\n centers[11] = (0.1072, 0.9596)\n centers[12] = (0.4173, 0.1690)\n centers[13] = (0.7262, 0.3251)\n centers[14] = (0.2432, 0.8277)\n centers[15] = (1.0000, 0.9355)\n centers[16] = (0.9563, 0.3611)\n centers[17] = (0.5110, 0.8462)\n centers[18] = (0.0000, 0.2778)\n centers[19] = (0.0909, 0.7218)\n centers[20] = (0.9595, 0.4332)\n centers[21] = (0.9164, 0.3579)\n centers[22] = (0.8150, 0.6342)\n centers[23] = (0.7024, 1.0000)\n centers[24] = (0.8636, 0.1273)\n centers[25] = (0.9218, 0.0000)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.3931247645645037}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_91/results/packing_viz.png
+ execution_time_mean: 390.1698920670897
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_91/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_91/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..8229b2edd34e0f9ced973ae41476d2d092524000
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_91/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.3931247645645037,
+ "public": {
+ "centers_str": " centers[0] = (0.0000, 0.3445)\n centers[1] = (0.1473, 0.2934)\n centers[2] = (0.0580, 0.0602)\n centers[3] = (0.0000, 0.2801)\n centers[4] = (0.0000, 1.0000)\n centers[5] = (0.0685, 0.6459)\n centers[6] = (0.3206, 0.0000)\n centers[7] = (0.0000, 0.0000)\n centers[8] = (0.6862, 0.0000)\n centers[9] = (0.1397, 0.8604)\n centers[10] = (0.3234, 0.5321)\n centers[11] = (0.1072, 0.9596)\n centers[12] = (0.4173, 0.1690)\n centers[13] = (0.7262, 0.3251)\n centers[14] = (0.2432, 0.8277)\n centers[15] = (1.0000, 0.9355)\n centers[16] = (0.9563, 0.3611)\n centers[17] = (0.5110, 0.8462)\n centers[18] = (0.0000, 0.2778)\n centers[19] = (0.0909, 0.7218)\n centers[20] = (0.9595, 0.4332)\n centers[21] = (0.9164, 0.3579)\n centers[22] = (0.8150, 0.6342)\n centers[23] = (0.7024, 1.0000)\n centers[24] = (0.8636, 0.1273)\n centers[25] = (0.9218, 0.0000)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.3931247645645037
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_91/results/packing_viz.png",
+ "execution_time_mean": 390.1698920670897,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..87611d2b06bf9ada9a2e036fb985b843c308f8ba
Binary files /dev/null and b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/correct.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/job_log.err b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/job_log.out b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..b4968940424bbb4d5706b27e2393a58739820ef8
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_circle_packing_WITH_vision_20260114_065819/gen_96/main.py
+Saving results to: results_circle_packing_WITH_vision_20260114_065819/gen_96/results
+Run 1/1 completed in 496.56 seconds
+Detailed packing data saved to results_circle_packing_WITH_vision_20260114_065819/gen_96/results/extra.npz
+Visualization saved to results_circle_packing_WITH_vision_20260114_065819/gen_96/results/packing_viz.png
+Correctness and error status saved to results_circle_packing_WITH_vision_20260114_065819/gen_96/results/correct.json
+Metrics saved to results_circle_packing_WITH_vision_20260114_065819/gen_96/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 2.487928081410891
+ public: {'centers_str': ' centers[0] = (0.0521, 0.0521)\n centers[1] = (0.3692, 0.0500)\n centers[2] = (0.0788, 0.1802)\n centers[3] = (0.0636, 0.5002)\n centers[4] = (0.0535, 0.7473)\n centers[5] = (0.0653, 0.6291)\n centers[6] = (0.5004, 0.0873)\n centers[7] = (0.1679, 0.0670)\n centers[8] = (0.4293, 0.4185)\n centers[9] = (0.0902, 0.3488)\n centers[10] = (0.1054, 0.8960)\n centers[11] = (0.3188, 0.8905)\n centers[12] = (0.2748, 0.0445)\n centers[13] = (0.5436, 0.2726)\n centers[14] = (0.2971, 0.2334)\n centers[15] = (0.7121, 0.6089)\n centers[16] = (0.2024, 0.7185)\n centers[17] = (0.5711, 0.8942)\n centers[18] = (0.6916, 0.1215)\n centers[19] = (0.5937, 0.4567)\n centers[20] = (0.2403, 0.4866)\n centers[21] = (0.4522, 0.6578)\n centers[22] = (0.9089, 0.6097)\n centers[23] = (0.8284, 0.8437)\n centers[24] = (0.8992, 0.1008)\n centers[25] = (0.8312, 0.3617)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 2.487928081410891}
+ visualization_path: results_circle_packing_WITH_vision_20260114_065819/gen_96/results/packing_viz.png
+ execution_time_mean: 496.55807940708473
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/metrics.json b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..234abc9922e15553ef57da94d27cd6df780844e9
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_circle_packing_WITH_vision_20260114_065819/gen_96/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 2.487928081410891,
+ "public": {
+ "centers_str": " centers[0] = (0.0521, 0.0521)\n centers[1] = (0.3692, 0.0500)\n centers[2] = (0.0788, 0.1802)\n centers[3] = (0.0636, 0.5002)\n centers[4] = (0.0535, 0.7473)\n centers[5] = (0.0653, 0.6291)\n centers[6] = (0.5004, 0.0873)\n centers[7] = (0.1679, 0.0670)\n centers[8] = (0.4293, 0.4185)\n centers[9] = (0.0902, 0.3488)\n centers[10] = (0.1054, 0.8960)\n centers[11] = (0.3188, 0.8905)\n centers[12] = (0.2748, 0.0445)\n centers[13] = (0.5436, 0.2726)\n centers[14] = (0.2971, 0.2334)\n centers[15] = (0.7121, 0.6089)\n centers[16] = (0.2024, 0.7185)\n centers[17] = (0.5711, 0.8942)\n centers[18] = (0.6916, 0.1215)\n centers[19] = (0.5937, 0.4567)\n centers[20] = (0.2403, 0.4866)\n centers[21] = (0.4522, 0.6578)\n centers[22] = (0.9089, 0.6097)\n centers[23] = (0.8284, 0.8437)\n centers[24] = (0.8992, 0.1008)\n centers[25] = (0.8312, 0.3617)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 2.487928081410891
+ },
+ "visualization_path": "results_circle_packing_WITH_vision_20260114_065819/gen_96/results/packing_viz.png",
+ "execution_time_mean": 496.55807940708473,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_vision_test/best/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_vision_test/best/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8304471cf64b9432f99f25db86a23eb84d256ce2
Binary files /dev/null and b/examples_deprecated/circle_packing/results_vision_test/best/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_vision_test/best/main.py b/examples_deprecated/circle_packing/results_vision_test/best/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..9306383d5dbb8671983adb69c529a06d51537b62
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/best/main.py
@@ -0,0 +1,94 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place circles in a structured pattern
+ # This is a simple pattern - evolution will improve this
+
+ # First, place a large circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles around it in a ring
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
+
+ # Place 16 more circles in an outer ring
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
+
+ # Additional positioning adjustment to make sure all circles
+ # are inside the square and don't overlap
+ # Clip to ensure everything is inside the unit square
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_vision_test/best/results/correct.json b/examples_deprecated/circle_packing/results_vision_test/best/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/best/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_vision_test/best/results/job_log.err b/examples_deprecated/circle_packing/results_vision_test/best/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/best/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_vision_test/best/results/job_log.out b/examples_deprecated/circle_packing/results_vision_test/best/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..0e26f45b7af239006e714d31039cdfe66ac47dfa
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/best/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_vision_test/gen_0/main.py
+Saving results to: results_vision_test/gen_0/results
+Run 1/1 completed in 0.00 seconds
+Detailed packing data saved to results_vision_test/gen_0/results/extra.npz
+Visualization saved to results_vision_test/gen_0/results/packing_viz.png
+Correctness and error status saved to results_vision_test/gen_0/results/correct.json
+Metrics saved to results_vision_test/gen_0/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 0.9597642169962064
+ public: {'centers_str': ' centers[0] = (0.5000, 0.5000)\n centers[1] = (0.8000, 0.5000)\n centers[2] = (0.7121, 0.7121)\n centers[3] = (0.5000, 0.8000)\n centers[4] = (0.2879, 0.7121)\n centers[5] = (0.2000, 0.5000)\n centers[6] = (0.2879, 0.2879)\n centers[7] = (0.5000, 0.2000)\n centers[8] = (0.7121, 0.2879)\n centers[9] = (0.9900, 0.5000)\n centers[10] = (0.9900, 0.7679)\n centers[11] = (0.9900, 0.9900)\n centers[12] = (0.7679, 0.9900)\n centers[13] = (0.5000, 0.9900)\n centers[14] = (0.2321, 0.9900)\n centers[15] = (0.0100, 0.9900)\n centers[16] = (0.0100, 0.7679)\n centers[17] = (0.0100, 0.5000)\n centers[18] = (0.0100, 0.2321)\n centers[19] = (0.0100, 0.0100)\n centers[20] = (0.2321, 0.0100)\n centers[21] = (0.5000, 0.0100)\n centers[22] = (0.7679, 0.0100)\n centers[23] = (0.9900, 0.0100)\n centers[24] = (0.9900, 0.2321)\n centers[25] = (0.0100, 0.0100)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 0.9597642169962064}
+ visualization_path: results_vision_test/gen_0/results/packing_viz.png
+ execution_time_mean: 0.0019530411809682846
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_vision_test/best/results/metrics.json b/examples_deprecated/circle_packing/results_vision_test/best/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..d6b447c8d1a45cb7d1329d14827f12d79f9f58c7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/best/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 0.9597642169962064,
+ "public": {
+ "centers_str": " centers[0] = (0.5000, 0.5000)\n centers[1] = (0.8000, 0.5000)\n centers[2] = (0.7121, 0.7121)\n centers[3] = (0.5000, 0.8000)\n centers[4] = (0.2879, 0.7121)\n centers[5] = (0.2000, 0.5000)\n centers[6] = (0.2879, 0.2879)\n centers[7] = (0.5000, 0.2000)\n centers[8] = (0.7121, 0.2879)\n centers[9] = (0.9900, 0.5000)\n centers[10] = (0.9900, 0.7679)\n centers[11] = (0.9900, 0.9900)\n centers[12] = (0.7679, 0.9900)\n centers[13] = (0.5000, 0.9900)\n centers[14] = (0.2321, 0.9900)\n centers[15] = (0.0100, 0.9900)\n centers[16] = (0.0100, 0.7679)\n centers[17] = (0.0100, 0.5000)\n centers[18] = (0.0100, 0.2321)\n centers[19] = (0.0100, 0.0100)\n centers[20] = (0.2321, 0.0100)\n centers[21] = (0.5000, 0.0100)\n centers[22] = (0.7679, 0.0100)\n centers[23] = (0.9900, 0.0100)\n centers[24] = (0.9900, 0.2321)\n centers[25] = (0.0100, 0.0100)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 0.9597642169962064
+ },
+ "visualization_path": "results_vision_test/gen_0/results/packing_viz.png",
+ "execution_time_mean": 0.0019530411809682846,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_0/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_vision_test/gen_0/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8304471cf64b9432f99f25db86a23eb84d256ce2
Binary files /dev/null and b/examples_deprecated/circle_packing/results_vision_test/gen_0/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_0/main.py b/examples_deprecated/circle_packing/results_vision_test/gen_0/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..9306383d5dbb8671983adb69c529a06d51537b62
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_0/main.py
@@ -0,0 +1,94 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place circles in a structured pattern
+ # This is a simple pattern - evolution will improve this
+
+ # First, place a large circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles around it in a ring
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
+
+ # Place 16 more circles in an outer ring
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
+
+ # Additional positioning adjustment to make sure all circles
+ # are inside the square and don't overlap
+ # Clip to ensure everything is inside the unit square
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_0/results/correct.json b/examples_deprecated/circle_packing/results_vision_test/gen_0/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_0/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_0/results/job_log.err b/examples_deprecated/circle_packing/results_vision_test/gen_0/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_0/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_0/results/job_log.out b/examples_deprecated/circle_packing/results_vision_test/gen_0/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..0e26f45b7af239006e714d31039cdfe66ac47dfa
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_0/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_vision_test/gen_0/main.py
+Saving results to: results_vision_test/gen_0/results
+Run 1/1 completed in 0.00 seconds
+Detailed packing data saved to results_vision_test/gen_0/results/extra.npz
+Visualization saved to results_vision_test/gen_0/results/packing_viz.png
+Correctness and error status saved to results_vision_test/gen_0/results/correct.json
+Metrics saved to results_vision_test/gen_0/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 0.9597642169962064
+ public: {'centers_str': ' centers[0] = (0.5000, 0.5000)\n centers[1] = (0.8000, 0.5000)\n centers[2] = (0.7121, 0.7121)\n centers[3] = (0.5000, 0.8000)\n centers[4] = (0.2879, 0.7121)\n centers[5] = (0.2000, 0.5000)\n centers[6] = (0.2879, 0.2879)\n centers[7] = (0.5000, 0.2000)\n centers[8] = (0.7121, 0.2879)\n centers[9] = (0.9900, 0.5000)\n centers[10] = (0.9900, 0.7679)\n centers[11] = (0.9900, 0.9900)\n centers[12] = (0.7679, 0.9900)\n centers[13] = (0.5000, 0.9900)\n centers[14] = (0.2321, 0.9900)\n centers[15] = (0.0100, 0.9900)\n centers[16] = (0.0100, 0.7679)\n centers[17] = (0.0100, 0.5000)\n centers[18] = (0.0100, 0.2321)\n centers[19] = (0.0100, 0.0100)\n centers[20] = (0.2321, 0.0100)\n centers[21] = (0.5000, 0.0100)\n centers[22] = (0.7679, 0.0100)\n centers[23] = (0.9900, 0.0100)\n centers[24] = (0.9900, 0.2321)\n centers[25] = (0.0100, 0.0100)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 0.9597642169962064}
+ visualization_path: results_vision_test/gen_0/results/packing_viz.png
+ execution_time_mean: 0.0019530411809682846
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_0/results/metrics.json b/examples_deprecated/circle_packing/results_vision_test/gen_0/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..d6b447c8d1a45cb7d1329d14827f12d79f9f58c7
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_0/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 0.9597642169962064,
+ "public": {
+ "centers_str": " centers[0] = (0.5000, 0.5000)\n centers[1] = (0.8000, 0.5000)\n centers[2] = (0.7121, 0.7121)\n centers[3] = (0.5000, 0.8000)\n centers[4] = (0.2879, 0.7121)\n centers[5] = (0.2000, 0.5000)\n centers[6] = (0.2879, 0.2879)\n centers[7] = (0.5000, 0.2000)\n centers[8] = (0.7121, 0.2879)\n centers[9] = (0.9900, 0.5000)\n centers[10] = (0.9900, 0.7679)\n centers[11] = (0.9900, 0.9900)\n centers[12] = (0.7679, 0.9900)\n centers[13] = (0.5000, 0.9900)\n centers[14] = (0.2321, 0.9900)\n centers[15] = (0.0100, 0.9900)\n centers[16] = (0.0100, 0.7679)\n centers[17] = (0.0100, 0.5000)\n centers[18] = (0.0100, 0.2321)\n centers[19] = (0.0100, 0.0100)\n centers[20] = (0.2321, 0.0100)\n centers[21] = (0.5000, 0.0100)\n centers[22] = (0.7679, 0.0100)\n centers[23] = (0.9900, 0.0100)\n centers[24] = (0.9900, 0.2321)\n centers[25] = (0.0100, 0.0100)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 0.9597642169962064
+ },
+ "visualization_path": "results_vision_test/gen_0/results/packing_viz.png",
+ "execution_time_mean": 0.0019530411809682846,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_1/__pycache__/main.cpython-311.pyc b/examples_deprecated/circle_packing/results_vision_test/gen_1/__pycache__/main.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..225e5a227da78908c6bd6acb90f798dfb25b1c52
Binary files /dev/null and b/examples_deprecated/circle_packing/results_vision_test/gen_1/__pycache__/main.cpython-311.pyc differ
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_1/edit.diff b/examples_deprecated/circle_packing/results_vision_test/gen_1/edit.diff
new file mode 100644
index 0000000000000000000000000000000000000000..e022fe24880ab3287d26c351839239308d22faca
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_1/edit.diff
@@ -0,0 +1,104 @@
+--- a/original.py
++++ b/original.py
+@@ -1,94 +1,96 @@
+ # EVOLVE-BLOCK-START
+ """Constructor-based circle packing for n=26 circles"""
+
+ import numpy as np
+
+
+ def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place circles in a structured pattern
+ # This is a simple pattern - evolution will improve this
+
+ # First, place a large circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles around it in a ring
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
+
+ # Place 16 more circles in an outer ring
++ # Adjusted radius (0.45) to ensure centers are within [0.05, 0.95]
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+- centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
++ centers[i + 9] = [0.5 + 0.45 * np.cos(angle), 0.5 + 0.45 * np.sin(angle)]
+
+- # Additional positioning adjustment to make sure all circles
+- # are inside the square and don't overlap
+- # Clip to ensure everything is inside the unit square
+- centers = np.clip(centers, 0.01, 0.99)
++ # Place the 26th circle uniquely, as the previous logic only placed 25 and had a duplicate at 0,0 (clipped to 0.01, 0.01)
++ centers[25] = [0.95, 0.95] # A placeholder in the top-right corner, ensuring it's within bounds
++
++ # Removed aggressive clipping, compute_max_radii will now correctly handle
++ # border constraints for centers that are within the [0,1] range.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+ def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ return radii
+
+
+ # EVOLVE-BLOCK-END
+
+
+ # This part remains fixed (not evolved)
+ def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_1/main.py b/examples_deprecated/circle_packing/results_vision_test/gen_1/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..28b90321f80c945b041c7c91aadb5a0d1b347e6b
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_1/main.py
@@ -0,0 +1,96 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place circles in a structured pattern
+ # This is a simple pattern - evolution will improve this
+
+ # First, place a large circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles around it in a ring
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
+
+ # Place 16 more circles in an outer ring
+ # Adjusted radius (0.45) to ensure centers are within [0.05, 0.95]
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+ centers[i + 9] = [0.5 + 0.45 * np.cos(angle), 0.5 + 0.45 * np.sin(angle)]
+
+ # Place the 26th circle uniquely, as the previous logic only placed 25 and had a duplicate at 0,0 (clipped to 0.01, 0.01)
+ centers[25] = [0.95, 0.95] # A placeholder in the top-right corner, ensuring it's within bounds
+
+ # Removed aggressive clipping, compute_max_radii will now correctly handle
+ # border constraints for centers that are within the [0,1] range.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_1/original.py b/examples_deprecated/circle_packing/results_vision_test/gen_1/original.py
new file mode 100644
index 0000000000000000000000000000000000000000..f52133ed39da8e9d823b52b994ee5063132aad94
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_1/original.py
@@ -0,0 +1,94 @@
+# EVOLVE-BLOCK-START
+"""Constructor-based circle packing for n=26 circles"""
+
+import numpy as np
+
+
+def construct_packing():
+ """
+ Construct a specific arrangement of 26 circles in a unit square
+ that attempts to maximize the sum of their radii.
+
+ Returns:
+ Tuple of (centers, radii, sum_of_radii)
+ centers: np.array of shape (26, 2) with (x, y) coordinates
+ radii: np.array of shape (26) with radius of each circle
+ sum_of_radii: Sum of all radii
+ """
+ # Initialize arrays for 26 circles
+ n = 26
+ centers = np.zeros((n, 2))
+
+ # Place circles in a structured pattern
+ # This is a simple pattern - evolution will improve this
+
+ # First, place a large circle in the center
+ centers[0] = [0.5, 0.5]
+
+ # Place 8 circles around it in a ring
+ for i in range(8):
+ angle = 2 * np.pi * i / 8
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
+
+ # Place 16 more circles in an outer ring
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
+
+ # Additional positioning adjustment to make sure all circles
+ # are inside the square and don't overlap
+ # Clip to ensure everything is inside the unit square
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+
+
+def compute_max_radii(centers):
+ """
+ Compute the maximum possible radii for each circle position
+ such that they don't overlap and stay within the unit square.
+
+ Args:
+ centers: np.array of shape (n, 2) with (x, y) coordinates
+
+ Returns:
+ np.array of shape (n) with radius of each circle
+ """
+ n = centers.shape[0]
+ radii = np.ones(n)
+
+ # First, limit by distance to square borders
+ for i in range(n):
+ x, y = centers[i]
+ # Distance to borders
+ radii[i] = min(x, y, 1 - x, 1 - y)
+
+ # Then, limit by distance to other circles
+ # Each pair of circles with centers at distance d can have
+ # sum of radii at most d to avoid overlap
+ for i in range(n):
+ for j in range(i + 1, n):
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
+
+ # If current radii would cause overlap
+ if radii[i] + radii[j] > dist:
+ # Scale both radii proportionally
+ scale = dist / (radii[i] + radii[j])
+ radii[i] *= scale
+ radii[j] *= scale
+
+ return radii
+
+
+# EVOLVE-BLOCK-END
+
+
+# This part remains fixed (not evolved)
+def run_packing():
+ """Run the circle packing constructor for n=26"""
+ centers, radii = construct_packing()
+ # Calculate the sum of radii
+ sum_radii = np.sum(radii)
+ return centers, radii, sum_radii
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_1/results/correct.json b/examples_deprecated/circle_packing/results_vision_test/gen_1/results/correct.json
new file mode 100644
index 0000000000000000000000000000000000000000..3253af2e0d1b8942bcb0602218b9caf2145945ab
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_1/results/correct.json
@@ -0,0 +1,4 @@
+{
+ "correct": true,
+ "error": null
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_1/results/job_log.err b/examples_deprecated/circle_packing/results_vision_test/gen_1/results/job_log.err
new file mode 100644
index 0000000000000000000000000000000000000000..345388018093ce84dec98ead3e182d85d46d64b2
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_1/results/job_log.err
@@ -0,0 +1,9 @@
+/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
+
+All support for the `google.generativeai` package has ended. It will no longer be receiving
+updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
+See README for more details:
+
+https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
+
+ import google.generativeai as genai # type: ignore[import-not-found]
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_1/results/job_log.out b/examples_deprecated/circle_packing/results_vision_test/gen_1/results/job_log.out
new file mode 100644
index 0000000000000000000000000000000000000000..05e5400da3cf5579fc8b67234ed892dd712a23e1
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_1/results/job_log.out
@@ -0,0 +1,18 @@
+Evaluating program: results_vision_test/gen_1/main.py
+Saving results to: results_vision_test/gen_1/results
+Run 1/1 completed in 0.00 seconds
+Detailed packing data saved to results_vision_test/gen_1/results/extra.npz
+Visualization saved to results_vision_test/gen_1/results/packing_viz.png
+Correctness and error status saved to results_vision_test/gen_1/results/correct.json
+Metrics saved to results_vision_test/gen_1/results/metrics.json
+Evaluation and Validation completed successfully.
+Metrics:
+ combined_score: 1.8777846733831447
+ public: {'centers_str': ' centers[0] = (0.5000, 0.5000)\n centers[1] = (0.8000, 0.5000)\n centers[2] = (0.7121, 0.7121)\n centers[3] = (0.5000, 0.8000)\n centers[4] = (0.2879, 0.7121)\n centers[5] = (0.2000, 0.5000)\n centers[6] = (0.2879, 0.2879)\n centers[7] = (0.5000, 0.2000)\n centers[8] = (0.7121, 0.2879)\n centers[9] = (0.9500, 0.5000)\n centers[10] = (0.9157, 0.6722)\n centers[11] = (0.8182, 0.8182)\n centers[12] = (0.6722, 0.9157)\n centers[13] = (0.5000, 0.9500)\n centers[14] = (0.3278, 0.9157)\n centers[15] = (0.1818, 0.8182)\n centers[16] = (0.0843, 0.6722)\n centers[17] = (0.0500, 0.5000)\n centers[18] = (0.0843, 0.3278)\n centers[19] = (0.1818, 0.1818)\n centers[20] = (0.3278, 0.0843)\n centers[21] = (0.5000, 0.0500)\n centers[22] = (0.6722, 0.0843)\n centers[23] = (0.8182, 0.1818)\n centers[24] = (0.9157, 0.3278)\n centers[25] = (0.9500, 0.9500)', 'num_circles': 26}
+ private: {'reported_sum_of_radii': 1.8777846733831447}
+ visualization_path: results_vision_test/gen_1/results/packing_viz.png
+ execution_time_mean: 0.001970076933503151
+ execution_time_std: 0.0
+ num_valid_runs: 1
+ num_invalid_runs: 0
+ all_validation_errors: []
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_1/results/metrics.json b/examples_deprecated/circle_packing/results_vision_test/gen_1/results/metrics.json
new file mode 100644
index 0000000000000000000000000000000000000000..1b8b94ae4d58aef1e19fe024e22b9271d3457d0f
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_1/results/metrics.json
@@ -0,0 +1,16 @@
+{
+ "combined_score": 1.8777846733831447,
+ "public": {
+ "centers_str": " centers[0] = (0.5000, 0.5000)\n centers[1] = (0.8000, 0.5000)\n centers[2] = (0.7121, 0.7121)\n centers[3] = (0.5000, 0.8000)\n centers[4] = (0.2879, 0.7121)\n centers[5] = (0.2000, 0.5000)\n centers[6] = (0.2879, 0.2879)\n centers[7] = (0.5000, 0.2000)\n centers[8] = (0.7121, 0.2879)\n centers[9] = (0.9500, 0.5000)\n centers[10] = (0.9157, 0.6722)\n centers[11] = (0.8182, 0.8182)\n centers[12] = (0.6722, 0.9157)\n centers[13] = (0.5000, 0.9500)\n centers[14] = (0.3278, 0.9157)\n centers[15] = (0.1818, 0.8182)\n centers[16] = (0.0843, 0.6722)\n centers[17] = (0.0500, 0.5000)\n centers[18] = (0.0843, 0.3278)\n centers[19] = (0.1818, 0.1818)\n centers[20] = (0.3278, 0.0843)\n centers[21] = (0.5000, 0.0500)\n centers[22] = (0.6722, 0.0843)\n centers[23] = (0.8182, 0.1818)\n centers[24] = (0.9157, 0.3278)\n centers[25] = (0.9500, 0.9500)",
+ "num_circles": 26
+ },
+ "private": {
+ "reported_sum_of_radii": 1.8777846733831447
+ },
+ "visualization_path": "results_vision_test/gen_1/results/packing_viz.png",
+ "execution_time_mean": 0.001970076933503151,
+ "execution_time_std": 0.0,
+ "num_valid_runs": 1,
+ "num_invalid_runs": 0,
+ "all_validation_errors": []
+}
\ No newline at end of file
diff --git a/examples_deprecated/circle_packing/results_vision_test/gen_1/search_replace.txt b/examples_deprecated/circle_packing/results_vision_test/gen_1/search_replace.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8e7adc00b3b95d76a42f583bf0de1b6e0844d873
--- /dev/null
+++ b/examples_deprecated/circle_packing/results_vision_test/gen_1/search_replace.txt
@@ -0,0 +1,47 @@
+
+The current implementation has several flaws that severely limit the achievable sum of radii:
+1. **Duplicate Circle**: The code implicitly places only 25 unique circles. The 26th circle (`centers[25]`) remains unassigned by the loops and defaults to `[0.0, 0.0]` from `np.zeros`, which is then clipped to `[0.01, 0.01]`. This results in a duplicate circle at `(0.01, 0.01)` (which is `centers[19]` after clipping as well). This wastes a circle slot.
+2. **Aggressive Clipping**: The line `centers = np.clip(centers, 0.01, 0.99)` forces circles near the boundaries to be at least `0.01` away from the edge. This severely restricts their potential radius, as `compute_max_radii` will immediately limit them to a radius of `0.01`. This is evident in the visualization where many tiny circles are clustered at the corners and edges.
+3. **Out-of-Bounds Centers**: The initial `0.7` factor for the outer ring calculation (e.g., `0.5 + 0.7 * np.cos(angle)`) results in center coordinates like `1.2` or `-0.2`, which are outside the `[0,1]` unit square. While `np.clip` currently "fixes" this, removing `np.clip` without adjusting the placement would cause `compute_max_radii` to produce invalid (negative) radii.
+
+This edit addresses these issues:
+1. **Removes `np.clip`**: This allows `compute_max_radii` to more accurately determine radii based on actual center positions and square boundaries, potentially yielding larger circles near the edges.
+2. **Adjusts Outer Ring Radius**: The factor `0.7` is changed to `0.45`. This ensures all calculated center coordinates for the outer ring fall within `[0.05, 0.95]`, thus keeping them validly within the `[0,1]` unit square after removing the clipping.
+3. **Adds Unique 26th Circle**: `centers[25]` is now explicitly placed at `[0.95, 0.95]`. This is a placeholder that ensures all 26 circles have unique, valid starting positions within the square, making the most of the available circle count. This specific corner was chosen as an initial guess to provide some space for a larger circle.
+
+These changes are fundamental to achieving higher sums of radii by enabling more accurate radius calculations and utilizing all 26 circles effectively.
+
+
+
+<<<<<<< SEARCH
+ # Place 16 more circles in an outer ring
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
+
+ # Additional positioning adjustment to make sure all circles
+ # are inside the square and don't overlap
+ # Clip to ensure everything is inside the unit square
+ centers = np.clip(centers, 0.01, 0.99)
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+=======
+ # Place 16 more circles in an outer ring
+ # Adjusted radius (0.45) to ensure centers are within [0.05, 0.95]
+ for i in range(16):
+ angle = 2 * np.pi * i / 16
+ centers[i + 9] = [0.5 + 0.45 * np.cos(angle), 0.5 + 0.45 * np.sin(angle)]
+
+ # Place the 26th circle uniquely, as the previous logic only placed 25 and had a duplicate at 0,0 (clipped to 0.01, 0.01)
+ centers[25] = [0.95, 0.95] # A placeholder in the top-right corner, ensuring it's within bounds
+
+ # Removed aggressive clipping, compute_max_radii will now correctly handle
+ # border constraints for centers that are within the [0,1] range.
+
+ # Compute maximum valid radii for this configuration
+ radii = compute_max_radii(centers)
+ return centers, radii
+>>>>>>> REPLACE
+
\ No newline at end of file