JustinTX commited on
Commit
66ffa33
·
verified ·
1 Parent(s): b3a284b

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/__pycache__/main.cpython-311.pyc +0 -0
  2. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/edit.diff +235 -0
  3. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/main.py +222 -0
  4. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/original.py +216 -0
  5. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/correct.json +4 -0
  6. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/job_log.err +9 -0
  7. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/job_log.out +18 -0
  8. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/metrics.json +16 -0
  9. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/search_replace.txt +93 -0
  10. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/__pycache__/main.cpython-311.pyc +0 -0
  11. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/main.py +94 -0
  12. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/correct.json +4 -0
  13. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/job_log.err +9 -0
  14. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/job_log.out +18 -0
  15. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/metrics.json +16 -0
  16. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/__pycache__/main.cpython-311.pyc +0 -0
  17. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/edit.diff +125 -0
  18. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/main.py +95 -0
  19. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/original.py +94 -0
  20. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/correct.json +4 -0
  21. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/job_log.err +9 -0
  22. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/job_log.out +18 -0
  23. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/metrics.json +16 -0
  24. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/search_replace.txt +185 -0
  25. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/__pycache__/main.cpython-311.pyc +0 -0
  26. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/edit.diff +145 -0
  27. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/main.py +124 -0
  28. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/original.py +128 -0
  29. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/correct.json +4 -0
  30. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/job_log.err +9 -0
  31. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/job_log.out +18 -0
  32. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/metrics.json +16 -0
  33. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/search_replace.txt +60 -0
  34. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/__pycache__/main.cpython-311.pyc +0 -0
  35. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/edit.diff +508 -0
  36. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/main.py +250 -0
  37. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/original.py +306 -0
  38. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results/job_log.err +19 -0
  39. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results/job_log.out +2 -0
  40. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/rewrite.txt +241 -0
  41. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/__pycache__/main.cpython-311.pyc +0 -0
  42. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/edit.diff +457 -0
  43. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/main.py +315 -0
  44. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/original.py +283 -0
  45. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/correct.json +4 -0
  46. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/job_log.err +9 -0
  47. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/job_log.out +18 -0
  48. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/metrics.json +16 -0
  49. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/rewrite.txt +306 -0
  50. examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/__pycache__/main.cpython-311.pyc +0 -0
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/__pycache__/main.cpython-311.pyc ADDED
Binary file (10.6 kB). View file
 
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/edit.diff ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,216 +1,222 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+ import math
7
+ import random
8
+
9
+ def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
10
+ """
11
+ Compute maximum radii using iterative proportional scaling.
12
+ This version is optimized for speed during search, with a final high-precision call.
13
+ """
14
+ n = centers.shape[0]
15
+ if n == 0:
16
+ return np.array([])
17
+
18
+ radii = np.min([
19
+ centers[:, 0], 1 - centers[:, 0],
20
+ centers[:, 1], 1 - centers[:, 1]
21
+ ], axis=0)
22
+
23
+ for _ in range(max_iter):
24
+ old_radii = radii.copy()
25
+ updated = False
26
+ for i in range(n):
27
+ for j in range(i + 1, n):
28
+ dist_sq = np.sum((centers[i] - centers[j])**2)
29
+ dist = np.sqrt(dist_sq)
30
+
31
+ if radii[i] + radii[j] > dist:
32
+ if dist < 1e-9:
33
+ radii[i], radii[j] = 0.0, 0.0
34
+ else:
35
+ scale = dist / (radii[i] + radii[j])
36
+ radii[i] *= scale
37
+ radii[j] *= scale
38
+ updated = True
39
+
40
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
41
+ break
42
+
43
+ return np.maximum(radii, 0)
44
+
45
+
46
+ class SimulatedAnnealer:
47
+ """
48
+ Performs a search for an optimal circle packing using Simulated Annealing.
49
+ """
50
+ def __init__(self, n, initial_centers, config):
51
+ self.n = n
52
+ self.config = config
53
+ self.centers = initial_centers
54
+
55
+ self.temp = config['t_start']
56
+ self.max_step_size = config['max_step_size']
57
+
58
+ self.energy = self._calculate_energy(self.centers)
59
+ self.best_centers = self.centers.copy()
60
+ self.best_energy = self.energy
61
+
62
+ self.move_weights = config['move_weights']
63
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
64
+
65
+ def _calculate_energy(self, centers):
66
+ """Energy is the negative sum of radii, to be minimized."""
67
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
68
+ return -np.sum(radii)
69
+
70
+ def _propose_move(self):
71
+ """
72
+ Proposes a new state by selecting a move type and applying it.
73
+ The step size is annealed with the temperature.
74
+ """
75
+ # Choose a move type based on weights
76
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
77
+
78
+ # Anneal step size
79
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
80
+ current_step_size = self.max_step_size * t_progress
81
+
82
+ return move_func(current_step_size)
83
+
84
+ def _move_single_circle(self, step_size):
85
+ """Move a single randomly chosen circle."""
86
+ new_centers = self.centers.copy()
87
+ idx = random.randint(0, self.n - 1)
88
+
89
+ displacement = np.random.normal(0, step_size, size=2)
90
+ new_centers[idx] += displacement
91
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
92
+ return new_centers
93
+
94
+ def _move_cluster(self, step_size):
95
+ """Move a small cluster of neighboring circles."""
96
+ new_centers = self.centers.copy()
97
+ cluster_size = self.config['cluster_size']
98
+
99
+ # Pick a seed circle
100
+ seed_idx = random.randint(0, self.n - 1)
101
+
102
+ # Find its neighbors
103
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
104
+ neighbor_indices = np.argsort(dists)[:cluster_size]
105
+
106
+ # Apply a common displacement
107
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
108
+ new_centers[neighbor_indices] += displacement
109
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
110
+ return new_centers
111
+
112
+ def _swap_circles(self, step_size):
113
+ """Swap the positions of two randomly chosen circles."""
114
+ new_centers = self.centers.copy()
115
+ if self.n < 2: return new_centers
116
+
117
+ idx1, idx2 = random.sample(range(self.n), 2)
118
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
119
+ return new_centers
120
+
121
+ def run(self):
122
+ """Executes the simulated annealing search."""
123
+ while self.temp > self.config['t_end']:
124
+ for _ in range(self.config['moves_per_temp']):
125
+ # Propose a new configuration
126
+ new_centers = self._propose_move()
127
+
128
+ # Calculate its energy
129
+ new_energy = self._calculate_energy(new_centers)
130
+
131
+ delta_e = new_energy - self.energy
132
+
133
+ # Acceptance criterion
134
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
135
+ self.centers = new_centers
136
+ self.energy = new_energy
137
+
138
+ # Update best-ever solution
139
+ if self.energy < self.best_energy:
140
+ self.best_energy = self.energy
141
+ self.best_centers = self.centers.copy()
142
+
143
+ # Cool down the temperature
144
+ self.temp *= self.config['cooling_rate']
145
+
146
+ return self.best_centers
147
+
148
+
149
+ def construct_packing():
150
+ """
151
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
152
+ """
153
+ n = 26
154
+ config = {
155
+ - 'num_starts': 12, # Number of independent SA runs
156
+ - 't_start': 0.05, # Initial temperature
157
+ + 'num_starts': 16, # More starts to explore different basins
158
+ + 't_start': 0.06, # Higher initial temperature for more exploration
159
+ 't_end': 1e-6, # Final temperature
160
+ - 'cooling_rate': 0.998, # Geometric cooling factor
161
+ - 'moves_per_temp': 60, # Number of moves at each temperature step
162
+ - 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
163
+ + 'cooling_rate': 0.999, # Slower cooling for more thorough search
164
+ + 'moves_per_temp': 50, # Adjusted moves to balance longer schedule
165
+ + 'max_step_size': 0.20, # Larger initial step size
166
+ 'radius_iter': 250, # Radius calculation iterations during search
167
+ - 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
168
+ - 'cluster_size': 4,
169
+ + 'move_weights': [0.70, 0.20, 0.10], # Increased probability for powerful swap move
170
+ + 'cluster_size': 5, # Slightly larger clusters
171
+ }
172
+
173
+ best_overall_centers = None
174
+ best_overall_score = -1.0
175
+
176
+ # Multi-start loop
177
+ for i in range(config['num_starts']):
178
+ # --- Generate diverse initial configurations ---
179
+ initial_centers = np.zeros((n, 2))
180
+ - if i < config['num_starts'] // 2:
181
+ + # Prioritize structured starts (12 of 16), which are generally more promising
182
+ + if i < 12:
183
+ # Start with a perturbed 5x5 grid + 1 extra circle
184
+ num_cells_side = 5
185
+ spacing = 1.0 / num_cells_side
186
+ k = 0
187
+ for row in range(num_cells_side):
188
+ for col in range(num_cells_side):
189
+ initial_centers[k, 0] = (col + 0.5) * spacing
190
+ initial_centers[k, 1] = (row + 0.5) * spacing
191
+ k += 1
192
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
193
+
194
+ - # Place the 26th circle in a strategic corner or center
195
+ - extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
196
+ + # Use a richer set of candidate positions for the 26th circle
197
+ + extra_pos_candidates = [
198
+ + [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
199
+ + [0.5, 0.5], # Center
200
+ + [0.2, 0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8], # Grid interstitial (spacing=0.2)
201
+ + [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
202
+ + ]
203
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
204
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
205
+ else:
206
+ # Start with a purely random configuration
207
+ initial_centers = np.random.rand(n, 2)
208
+
209
+ # Run the annealer for this start
210
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
211
+ result_centers = solver.run()
212
+
213
+ # Evaluate and update the best overall result
214
+ radii = compute_max_radii(result_centers, max_iter=2000)
215
+ score = np.sum(radii)
216
+
217
+ if score > best_overall_score:
218
+ best_overall_score = score
219
+ best_overall_centers = result_centers
220
+
221
+ # Final high-precision radius calculation for the best solution found
222
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
223
+
224
+ return best_overall_centers, final_radii
225
+
226
+ # EVOLVE-BLOCK-END
227
+
228
+
229
+ # This part remains fixed (not evolved)
230
+ def run_packing():
231
+ """Run the circle packing constructor for n=26"""
232
+ centers, radii = construct_packing()
233
+ # Calculate the sum of radii
234
+ sum_radii = np.sum(radii)
235
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/main.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ import math
4
+ import random
5
+
6
+ def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
7
+ """
8
+ Compute maximum radii using iterative proportional scaling.
9
+ This version is optimized for speed during search, with a final high-precision call.
10
+ """
11
+ n = centers.shape[0]
12
+ if n == 0:
13
+ return np.array([])
14
+
15
+ radii = np.min([
16
+ centers[:, 0], 1 - centers[:, 0],
17
+ centers[:, 1], 1 - centers[:, 1]
18
+ ], axis=0)
19
+
20
+ for _ in range(max_iter):
21
+ old_radii = radii.copy()
22
+ updated = False
23
+ for i in range(n):
24
+ for j in range(i + 1, n):
25
+ dist_sq = np.sum((centers[i] - centers[j])**2)
26
+ dist = np.sqrt(dist_sq)
27
+
28
+ if radii[i] + radii[j] > dist:
29
+ if dist < 1e-9:
30
+ radii[i], radii[j] = 0.0, 0.0
31
+ else:
32
+ scale = dist / (radii[i] + radii[j])
33
+ radii[i] *= scale
34
+ radii[j] *= scale
35
+ updated = True
36
+
37
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
38
+ break
39
+
40
+ return np.maximum(radii, 0)
41
+
42
+
43
+ class SimulatedAnnealer:
44
+ """
45
+ Performs a search for an optimal circle packing using Simulated Annealing.
46
+ """
47
+ def __init__(self, n, initial_centers, config):
48
+ self.n = n
49
+ self.config = config
50
+ self.centers = initial_centers
51
+
52
+ self.temp = config['t_start']
53
+ self.max_step_size = config['max_step_size']
54
+
55
+ self.energy = self._calculate_energy(self.centers)
56
+ self.best_centers = self.centers.copy()
57
+ self.best_energy = self.energy
58
+
59
+ self.move_weights = config['move_weights']
60
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
61
+
62
+ def _calculate_energy(self, centers):
63
+ """Energy is the negative sum of radii, to be minimized."""
64
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
65
+ return -np.sum(radii)
66
+
67
+ def _propose_move(self):
68
+ """
69
+ Proposes a new state by selecting a move type and applying it.
70
+ The step size is annealed with the temperature.
71
+ """
72
+ # Choose a move type based on weights
73
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
74
+
75
+ # Anneal step size
76
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
77
+ current_step_size = self.max_step_size * t_progress
78
+
79
+ return move_func(current_step_size)
80
+
81
+ def _move_single_circle(self, step_size):
82
+ """Move a single randomly chosen circle."""
83
+ new_centers = self.centers.copy()
84
+ idx = random.randint(0, self.n - 1)
85
+
86
+ displacement = np.random.normal(0, step_size, size=2)
87
+ new_centers[idx] += displacement
88
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
89
+ return new_centers
90
+
91
+ def _move_cluster(self, step_size):
92
+ """Move a small cluster of neighboring circles."""
93
+ new_centers = self.centers.copy()
94
+ cluster_size = self.config['cluster_size']
95
+
96
+ # Pick a seed circle
97
+ seed_idx = random.randint(0, self.n - 1)
98
+
99
+ # Find its neighbors
100
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
101
+ neighbor_indices = np.argsort(dists)[:cluster_size]
102
+
103
+ # Apply a common displacement
104
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
105
+ new_centers[neighbor_indices] += displacement
106
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
107
+ return new_centers
108
+
109
+ def _swap_circles(self, step_size):
110
+ """Swap the positions of two randomly chosen circles."""
111
+ new_centers = self.centers.copy()
112
+ if self.n < 2: return new_centers
113
+
114
+ idx1, idx2 = random.sample(range(self.n), 2)
115
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
116
+ return new_centers
117
+
118
+ def run(self):
119
+ """Executes the simulated annealing search."""
120
+ while self.temp > self.config['t_end']:
121
+ for _ in range(self.config['moves_per_temp']):
122
+ # Propose a new configuration
123
+ new_centers = self._propose_move()
124
+
125
+ # Calculate its energy
126
+ new_energy = self._calculate_energy(new_centers)
127
+
128
+ delta_e = new_energy - self.energy
129
+
130
+ # Acceptance criterion
131
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
132
+ self.centers = new_centers
133
+ self.energy = new_energy
134
+
135
+ # Update best-ever solution
136
+ if self.energy < self.best_energy:
137
+ self.best_energy = self.energy
138
+ self.best_centers = self.centers.copy()
139
+
140
+ # Cool down the temperature
141
+ self.temp *= self.config['cooling_rate']
142
+
143
+ return self.best_centers
144
+
145
+
146
+ def construct_packing():
147
+ """
148
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
149
+ """
150
+ n = 26
151
+ config = {
152
+ 'num_starts': 16, # More starts to explore different basins
153
+ 't_start': 0.06, # Higher initial temperature for more exploration
154
+ 't_end': 1e-6, # Final temperature
155
+ 'cooling_rate': 0.999, # Slower cooling for more thorough search
156
+ 'moves_per_temp': 50, # Adjusted moves to balance longer schedule
157
+ 'max_step_size': 0.20, # Larger initial step size
158
+ 'radius_iter': 250, # Radius calculation iterations during search
159
+ 'move_weights': [0.70, 0.20, 0.10], # Increased probability for powerful swap move
160
+ 'cluster_size': 5, # Slightly larger clusters
161
+ }
162
+
163
+ best_overall_centers = None
164
+ best_overall_score = -1.0
165
+
166
+ # Multi-start loop
167
+ for i in range(config['num_starts']):
168
+ # --- Generate diverse initial configurations ---
169
+ initial_centers = np.zeros((n, 2))
170
+ # Prioritize structured starts (12 of 16), which are generally more promising
171
+ if i < 12:
172
+ # Start with a perturbed 5x5 grid + 1 extra circle
173
+ num_cells_side = 5
174
+ spacing = 1.0 / num_cells_side
175
+ k = 0
176
+ for row in range(num_cells_side):
177
+ for col in range(num_cells_side):
178
+ initial_centers[k, 0] = (col + 0.5) * spacing
179
+ initial_centers[k, 1] = (row + 0.5) * spacing
180
+ k += 1
181
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
182
+
183
+ # Use a richer set of candidate positions for the 26th circle
184
+ extra_pos_candidates = [
185
+ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
186
+ [0.5, 0.5], # Center
187
+ [0.2, 0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8], # Grid interstitial (spacing=0.2)
188
+ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
189
+ ]
190
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
191
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
192
+ else:
193
+ # Start with a purely random configuration
194
+ initial_centers = np.random.rand(n, 2)
195
+
196
+ # Run the annealer for this start
197
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
198
+ result_centers = solver.run()
199
+
200
+ # Evaluate and update the best overall result
201
+ radii = compute_max_radii(result_centers, max_iter=2000)
202
+ score = np.sum(radii)
203
+
204
+ if score > best_overall_score:
205
+ best_overall_score = score
206
+ best_overall_centers = result_centers
207
+
208
+ # Final high-precision radius calculation for the best solution found
209
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
210
+
211
+ return best_overall_centers, final_radii
212
+
213
+ # EVOLVE-BLOCK-END
214
+
215
+
216
+ # This part remains fixed (not evolved)
217
+ def run_packing():
218
+ """Run the circle packing constructor for n=26"""
219
+ centers, radii = construct_packing()
220
+ # Calculate the sum of radii
221
+ sum_radii = np.sum(radii)
222
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/original.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ import math
4
+ import random
5
+
6
+ def compute_max_radii(centers, max_iter=500, convergence_threshold=1e-7):
7
+ """
8
+ Compute maximum radii using iterative proportional scaling.
9
+ This version is optimized for speed during search, with a final high-precision call.
10
+ """
11
+ n = centers.shape[0]
12
+ if n == 0:
13
+ return np.array([])
14
+
15
+ radii = np.min([
16
+ centers[:, 0], 1 - centers[:, 0],
17
+ centers[:, 1], 1 - centers[:, 1]
18
+ ], axis=0)
19
+
20
+ for _ in range(max_iter):
21
+ old_radii = radii.copy()
22
+ updated = False
23
+ for i in range(n):
24
+ for j in range(i + 1, n):
25
+ dist_sq = np.sum((centers[i] - centers[j])**2)
26
+ dist = np.sqrt(dist_sq)
27
+
28
+ if radii[i] + radii[j] > dist:
29
+ if dist < 1e-9:
30
+ radii[i], radii[j] = 0.0, 0.0
31
+ else:
32
+ scale = dist / (radii[i] + radii[j])
33
+ radii[i] *= scale
34
+ radii[j] *= scale
35
+ updated = True
36
+
37
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
38
+ break
39
+
40
+ return np.maximum(radii, 0)
41
+
42
+
43
+ class SimulatedAnnealer:
44
+ """
45
+ Performs a search for an optimal circle packing using Simulated Annealing.
46
+ """
47
+ def __init__(self, n, initial_centers, config):
48
+ self.n = n
49
+ self.config = config
50
+ self.centers = initial_centers
51
+
52
+ self.temp = config['t_start']
53
+ self.max_step_size = config['max_step_size']
54
+
55
+ self.energy = self._calculate_energy(self.centers)
56
+ self.best_centers = self.centers.copy()
57
+ self.best_energy = self.energy
58
+
59
+ self.move_weights = config['move_weights']
60
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles]
61
+
62
+ def _calculate_energy(self, centers):
63
+ """Energy is the negative sum of radii, to be minimized."""
64
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
65
+ return -np.sum(radii)
66
+
67
+ def _propose_move(self):
68
+ """
69
+ Proposes a new state by selecting a move type and applying it.
70
+ The step size is annealed with the temperature.
71
+ """
72
+ # Choose a move type based on weights
73
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
74
+
75
+ # Anneal step size
76
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
77
+ current_step_size = self.max_step_size * t_progress
78
+
79
+ return move_func(current_step_size)
80
+
81
+ def _move_single_circle(self, step_size):
82
+ """Move a single randomly chosen circle."""
83
+ new_centers = self.centers.copy()
84
+ idx = random.randint(0, self.n - 1)
85
+
86
+ displacement = np.random.normal(0, step_size, size=2)
87
+ new_centers[idx] += displacement
88
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
89
+ return new_centers
90
+
91
+ def _move_cluster(self, step_size):
92
+ """Move a small cluster of neighboring circles."""
93
+ new_centers = self.centers.copy()
94
+ cluster_size = self.config['cluster_size']
95
+
96
+ # Pick a seed circle
97
+ seed_idx = random.randint(0, self.n - 1)
98
+
99
+ # Find its neighbors
100
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
101
+ neighbor_indices = np.argsort(dists)[:cluster_size]
102
+
103
+ # Apply a common displacement
104
+ displacement = np.random.normal(0, step_size * 0.5, size=2) # Smaller step for clusters
105
+ new_centers[neighbor_indices] += displacement
106
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
107
+ return new_centers
108
+
109
+ def _swap_circles(self, step_size):
110
+ """Swap the positions of two randomly chosen circles."""
111
+ new_centers = self.centers.copy()
112
+ if self.n < 2: return new_centers
113
+
114
+ idx1, idx2 = random.sample(range(self.n), 2)
115
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
116
+ return new_centers
117
+
118
+ def run(self):
119
+ """Executes the simulated annealing search."""
120
+ while self.temp > self.config['t_end']:
121
+ for _ in range(self.config['moves_per_temp']):
122
+ # Propose a new configuration
123
+ new_centers = self._propose_move()
124
+
125
+ # Calculate its energy
126
+ new_energy = self._calculate_energy(new_centers)
127
+
128
+ delta_e = new_energy - self.energy
129
+
130
+ # Acceptance criterion
131
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
132
+ self.centers = new_centers
133
+ self.energy = new_energy
134
+
135
+ # Update best-ever solution
136
+ if self.energy < self.best_energy:
137
+ self.best_energy = self.energy
138
+ self.best_centers = self.centers.copy()
139
+
140
+ # Cool down the temperature
141
+ self.temp *= self.config['cooling_rate']
142
+
143
+ return self.best_centers
144
+
145
+
146
+ def construct_packing():
147
+ """
148
+ Constructs a packing of 26 circles using a multi-start Simulated Annealing approach.
149
+ """
150
+ n = 26
151
+ config = {
152
+ 'num_starts': 12, # Number of independent SA runs
153
+ 't_start': 0.05, # Initial temperature
154
+ 't_end': 1e-6, # Final temperature
155
+ 'cooling_rate': 0.998, # Geometric cooling factor
156
+ 'moves_per_temp': 60, # Number of moves at each temperature step
157
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
158
+ 'radius_iter': 250, # Radius calculation iterations during search
159
+ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
160
+ 'cluster_size': 4,
161
+ }
162
+
163
+ best_overall_centers = None
164
+ best_overall_score = -1.0
165
+
166
+ # Multi-start loop
167
+ for i in range(config['num_starts']):
168
+ # --- Generate diverse initial configurations ---
169
+ initial_centers = np.zeros((n, 2))
170
+ if i < config['num_starts'] // 2:
171
+ # Start with a perturbed 5x5 grid + 1 extra circle
172
+ num_cells_side = 5
173
+ spacing = 1.0 / num_cells_side
174
+ k = 0
175
+ for row in range(num_cells_side):
176
+ for col in range(num_cells_side):
177
+ initial_centers[k, 0] = (col + 0.5) * spacing
178
+ initial_centers[k, 1] = (row + 0.5) * spacing
179
+ k += 1
180
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
181
+
182
+ # Place the 26th circle in a strategic corner or center
183
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
184
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
185
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
186
+ else:
187
+ # Start with a purely random configuration
188
+ initial_centers = np.random.rand(n, 2)
189
+
190
+ # Run the annealer for this start
191
+ solver = SimulatedAnnealer(n=n, initial_centers=initial_centers, config=config)
192
+ result_centers = solver.run()
193
+
194
+ # Evaluate and update the best overall result
195
+ radii = compute_max_radii(result_centers, max_iter=2000)
196
+ score = np.sum(radii)
197
+
198
+ if score > best_overall_score:
199
+ best_overall_score = score
200
+ best_overall_centers = result_centers
201
+
202
+ # Final high-precision radius calculation for the best solution found
203
+ final_radii = compute_max_radii(best_overall_centers, max_iter=2000)
204
+
205
+ return best_overall_centers, final_radii
206
+
207
+ # EVOLVE-BLOCK-END
208
+
209
+
210
+ # This part remains fixed (not evolved)
211
+ def run_packing():
212
+ """Run the circle packing constructor for n=26"""
213
+ centers, radii = construct_packing()
214
+ # Calculate the sum of radii
215
+ sum_radii = np.sum(radii)
216
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/correct.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "correct": true,
3
+ "error": null
4
+ }
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/job_log.err ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
2
+
3
+ All support for the `google.generativeai` package has ended. It will no longer be receiving
4
+ updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
5
+ See README for more details:
6
+
7
+ https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
8
+
9
+ import google.generativeai as genai # type: ignore[import-not-found]
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/job_log.out ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/main.py
2
+ Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results
3
+ Run 1/1 completed in 32125.27 seconds
4
+ Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/extra.npz
5
+ Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/packing_viz.png
6
+ Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/correct.json
7
+ Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/metrics.json
8
+ Evaluation and Validation completed successfully.
9
+ Metrics:
10
+ combined_score: 2.5604436893300666
11
+ 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}
12
+ private: {'reported_sum_of_radii': 2.5604436893300666}
13
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/packing_viz.png
14
+ execution_time_mean: 32125.269347094
15
+ execution_time_std: 0.0
16
+ num_valid_runs: 1
17
+ num_invalid_runs: 0
18
+ all_validation_errors: []
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/results/metrics.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "combined_score": 2.5604436893300666,
3
+ "public": {
4
+ "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)",
5
+ "num_circles": 26
6
+ },
7
+ "private": {
8
+ "reported_sum_of_radii": 2.5604436893300666
9
+ },
10
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_106/results/packing_viz.png",
11
+ "execution_time_mean": 32125.269347094,
12
+ "execution_time_std": 0.0,
13
+ "num_valid_runs": 1,
14
+ "num_invalid_runs": 0,
15
+ "all_validation_errors": []
16
+ }
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/best/search_replace.txt ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 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.
2
+
3
+ <NAME>
4
+ refined_annealing_schedule
5
+ </NAME>
6
+ <DESCRIPTION>
7
+ This change adjusts the simulated annealing hyperparameters to promote a more extensive and effective search.
8
+ - **`num_starts`** is increased from 12 to 16 to explore more regions of the solution space.
9
+ - **`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.
10
+ - **`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.
11
+ - **`moves_per_temp`** is slightly reduced to balance the substantially longer schedule.
12
+ - **`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.
13
+ - **`cluster_size`** is increased to 5 to allow for slightly larger correlated movements.
14
+ Collectively, these changes create a more robust annealing process capable of finding higher-quality solutions.
15
+ </DESCRIPTION>
16
+ <DIFF>
17
+ <<<<<<< SEARCH
18
+ config = {
19
+ 'num_starts': 12, # Number of independent SA runs
20
+ 't_start': 0.05, # Initial temperature
21
+ 't_end': 1e-6, # Final temperature
22
+ 'cooling_rate': 0.998, # Geometric cooling factor
23
+ 'moves_per_temp': 60, # Number of moves at each temperature step
24
+ 'max_step_size': 0.15, # Max displacement for a single circle move at T_start
25
+ 'radius_iter': 250, # Radius calculation iterations during search
26
+ 'move_weights': [0.75, 0.20, 0.05], # Probabilities for [single, cluster, swap]
27
+ 'cluster_size': 4,
28
+ }
29
+ =======
30
+ config = {
31
+ 'num_starts': 16, # More starts to explore different basins
32
+ 't_start': 0.06, # Higher initial temperature for more exploration
33
+ 't_end': 1e-6, # Final temperature
34
+ 'cooling_rate': 0.999, # Slower cooling for more thorough search
35
+ 'moves_per_temp': 50, # Adjusted moves to balance longer schedule
36
+ 'max_step_size': 0.20, # Larger initial step size
37
+ 'radius_iter': 250, # Radius calculation iterations during search
38
+ 'move_weights': [0.70, 0.20, 0.10], # Increased probability for powerful swap move
39
+ 'cluster_size': 5, # Slightly larger clusters
40
+ }
41
+ >>>>>>> REPLACE
42
+ </DIFF>
43
+ <NAME>
44
+ diversify_initial_configurations
45
+ </NAME>
46
+ <DESCRIPTION>
47
+ 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.
48
+ </DESCRIPTION>
49
+ <DIFF>
50
+ <<<<<<< SEARCH
51
+ if i < config['num_starts'] // 2:
52
+ # Start with a perturbed 5x5 grid + 1 extra circle
53
+ num_cells_side = 5
54
+ spacing = 1.0 / num_cells_side
55
+ k = 0
56
+ for row in range(num_cells_side):
57
+ for col in range(num_cells_side):
58
+ initial_centers[k, 0] = (col + 0.5) * spacing
59
+ initial_centers[k, 1] = (row + 0.5) * spacing
60
+ k += 1
61
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
62
+
63
+ # Place the 26th circle in a strategic corner or center
64
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
65
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
66
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
67
+ else:
68
+ =======
69
+ # Prioritize structured starts (12 of 16), which are generally more promising
70
+ if i < 12:
71
+ # Start with a perturbed 5x5 grid + 1 extra circle
72
+ num_cells_side = 5
73
+ spacing = 1.0 / num_cells_side
74
+ k = 0
75
+ for row in range(num_cells_side):
76
+ for col in range(num_cells_side):
77
+ initial_centers[k, 0] = (col + 0.5) * spacing
78
+ initial_centers[k, 1] = (row + 0.5) * spacing
79
+ k += 1
80
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
81
+
82
+ # Use a richer set of candidate positions for the 26th circle
83
+ extra_pos_candidates = [
84
+ [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], # Corners (strong)
85
+ [0.5, 0.5], # Center
86
+ [0.2, 0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8], # Grid interstitial (spacing=0.2)
87
+ [0.5, 0.15], [0.15, 0.5], [0.85, 0.5], [0.5, 0.85] # Edge centers (offset)
88
+ ]
89
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
90
+ initial_centers = np.clip(initial_centers, 0.0, 1.0)
91
+ else:
92
+ >>>>>>> REPLACE
93
+ </DIFF>
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/__pycache__/main.cpython-311.pyc ADDED
Binary file (3.42 kB). View file
 
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/main.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """Constructor-based circle packing for n=26 circles"""
3
+
4
+ import numpy as np
5
+
6
+
7
+ def construct_packing():
8
+ """
9
+ Construct a specific arrangement of 26 circles in a unit square
10
+ that attempts to maximize the sum of their radii.
11
+
12
+ Returns:
13
+ Tuple of (centers, radii, sum_of_radii)
14
+ centers: np.array of shape (26, 2) with (x, y) coordinates
15
+ radii: np.array of shape (26) with radius of each circle
16
+ sum_of_radii: Sum of all radii
17
+ """
18
+ # Initialize arrays for 26 circles
19
+ n = 26
20
+ centers = np.zeros((n, 2))
21
+
22
+ # Place circles in a structured pattern
23
+ # This is a simple pattern - evolution will improve this
24
+
25
+ # First, place a large circle in the center
26
+ centers[0] = [0.5, 0.5]
27
+
28
+ # Place 8 circles around it in a ring
29
+ for i in range(8):
30
+ angle = 2 * np.pi * i / 8
31
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
32
+
33
+ # Place 16 more circles in an outer ring
34
+ for i in range(16):
35
+ angle = 2 * np.pi * i / 16
36
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
37
+
38
+ # Additional positioning adjustment to make sure all circles
39
+ # are inside the square and don't overlap
40
+ # Clip to ensure everything is inside the unit square
41
+ centers = np.clip(centers, 0.01, 0.99)
42
+
43
+ # Compute maximum valid radii for this configuration
44
+ radii = compute_max_radii(centers)
45
+ return centers, radii
46
+
47
+
48
+ def compute_max_radii(centers):
49
+ """
50
+ Compute the maximum possible radii for each circle position
51
+ such that they don't overlap and stay within the unit square.
52
+
53
+ Args:
54
+ centers: np.array of shape (n, 2) with (x, y) coordinates
55
+
56
+ Returns:
57
+ np.array of shape (n) with radius of each circle
58
+ """
59
+ n = centers.shape[0]
60
+ radii = np.ones(n)
61
+
62
+ # First, limit by distance to square borders
63
+ for i in range(n):
64
+ x, y = centers[i]
65
+ # Distance to borders
66
+ radii[i] = min(x, y, 1 - x, 1 - y)
67
+
68
+ # Then, limit by distance to other circles
69
+ # Each pair of circles with centers at distance d can have
70
+ # sum of radii at most d to avoid overlap
71
+ for i in range(n):
72
+ for j in range(i + 1, n):
73
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
74
+
75
+ # If current radii would cause overlap
76
+ if radii[i] + radii[j] > dist:
77
+ # Scale both radii proportionally
78
+ scale = dist / (radii[i] + radii[j])
79
+ radii[i] *= scale
80
+ radii[j] *= scale
81
+
82
+ return radii
83
+
84
+
85
+ # EVOLVE-BLOCK-END
86
+
87
+
88
+ # This part remains fixed (not evolved)
89
+ def run_packing():
90
+ """Run the circle packing constructor for n=26"""
91
+ centers, radii = construct_packing()
92
+ # Calculate the sum of radii
93
+ sum_radii = np.sum(radii)
94
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/correct.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "correct": true,
3
+ "error": null
4
+ }
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/job_log.err ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
2
+
3
+ All support for the `google.generativeai` package has ended. It will no longer be receiving
4
+ updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
5
+ See README for more details:
6
+
7
+ https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
8
+
9
+ import google.generativeai as genai # type: ignore[import-not-found]
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/job_log.out ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/main.py
2
+ Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results
3
+ Run 1/1 completed in 0.00 seconds
4
+ Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/extra.npz
5
+ Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/packing_viz.png
6
+ Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/correct.json
7
+ Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/metrics.json
8
+ Evaluation and Validation completed successfully.
9
+ Metrics:
10
+ combined_score: 0.9597642169962064
11
+ 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}
12
+ private: {'reported_sum_of_radii': 0.9597642169962064}
13
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/packing_viz.png
14
+ execution_time_mean: 0.00208668177947402
15
+ execution_time_std: 0.0
16
+ num_valid_runs: 1
17
+ num_invalid_runs: 0
18
+ all_validation_errors: []
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/metrics.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "combined_score": 0.9597642169962064,
3
+ "public": {
4
+ "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)",
5
+ "num_circles": 26
6
+ },
7
+ "private": {
8
+ "reported_sum_of_radii": 0.9597642169962064
9
+ },
10
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_0/results/packing_viz.png",
11
+ "execution_time_mean": 0.00208668177947402,
12
+ "execution_time_std": 0.0,
13
+ "num_valid_runs": 1,
14
+ "num_invalid_runs": 0,
15
+ "all_validation_errors": []
16
+ }
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/__pycache__/main.cpython-311.pyc ADDED
Binary file (3.1 kB). View file
 
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/edit.diff ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,94 +1,95 @@
4
+ # EVOLVE-BLOCK-START
5
+ """Constructor-based circle packing for n=26 circles"""
6
+
7
+ import numpy as np
8
+
9
+
10
+ def construct_packing():
11
+ """
12
+ Construct a specific arrangement of 26 circles in a unit square
13
+ that attempts to maximize the sum of their radii.
14
+
15
+ Returns:
16
+ Tuple of (centers, radii, sum_of_radii)
17
+ centers: np.array of shape (26, 2) with (x, y) coordinates
18
+ radii: np.array of shape (26) with radius of each circle
19
+ sum_of_radii: Sum of all radii
20
+ """
21
+ # Initialize arrays for 26 circles
22
+ n = 26
23
+ centers = np.zeros((n, 2))
24
+
25
+ - # Place circles in a structured pattern
26
+ - # This is a simple pattern - evolution will improve this
27
+ + # Place 25 circles in a 5x5 grid
28
+ + # This provides a more even distribution and better use of space
29
+ + num_cells_side = 5
30
+ + spacing = 1.0 / num_cells_side
31
+
32
+ - # First, place a large circle in the center
33
+ - centers[0] = [0.5, 0.5]
34
+ + k = 0
35
+ + for i in range(num_cells_side):
36
+ + for j in range(num_cells_side):
37
+ + centers[k, 0] = (i + 0.5) * spacing
38
+ + centers[k, 1] = (j + 0.5) * spacing
39
+ + k += 1
40
+
41
+ - # Place 8 circles around it in a ring
42
+ - for i in range(8):
43
+ - angle = 2 * np.pi * i / 8
44
+ - centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
45
+ + # Place the 26th circle in a corner, slightly offset to allow for radius.
46
+ + # This location is chosen to utilize corner space efficiently,
47
+ + # as the grid circles are not directly in the corners.
48
+ + centers[25] = [0.05, 0.05]
49
+
50
+ - # Place 16 more circles in an outer ring
51
+ - for i in range(16):
52
+ - angle = 2 * np.pi * i / 16
53
+ - centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
54
+ -
55
+ - # Additional positioning adjustment to make sure all circles
56
+ - # are inside the square and don't overlap
57
+ - # Clip to ensure everything is inside the unit square
58
+ - centers = np.clip(centers, 0.01, 0.99)
59
+ + # No need to clip centers, as they are intentionally placed within (0,1) range
60
+ + # and compute_max_radii handles boundary constraints.
61
+
62
+ # Compute maximum valid radii for this configuration
63
+ radii = compute_max_radii(centers)
64
+ return centers, radii
65
+
66
+
67
+ def compute_max_radii(centers):
68
+ """
69
+ Compute the maximum possible radii for each circle position
70
+ such that they don't overlap and stay within the unit square.
71
+
72
+ Args:
73
+ centers: np.array of shape (n, 2) with (x, y) coordinates
74
+
75
+ Returns:
76
+ np.array of shape (n) with radius of each circle
77
+ """
78
+ n = centers.shape[0]
79
+ radii = np.ones(n)
80
+
81
+ # First, limit by distance to square borders
82
+ for i in range(n):
83
+ x, y = centers[i]
84
+ # Distance to borders
85
+ radii[i] = min(x, y, 1 - x, 1 - y)
86
+
87
+ # Then, limit by distance to other circles
88
+ # Each pair of circles with centers at distance d can have
89
+ # sum of radii at most d to avoid overlap
90
+ - for i in range(n):
91
+ - for j in range(i + 1, n):
92
+ - dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
93
+ + # This iterative scaling is a greedy approach and the order can matter,
94
+ + # but for constructor-based approaches, it's a common method.
95
+ + for _ in range(100): # Iterate multiple times to stabilize radii
96
+ + for i in range(n):
97
+ + for j in range(i + 1, n):
98
+ + dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
99
+
100
+ - # If current radii would cause overlap
101
+ - if radii[i] + radii[j] > dist:
102
+ - # Scale both radii proportionally
103
+ - scale = dist / (radii[i] + radii[j])
104
+ - radii[i] *= scale
105
+ - radii[j] *= scale
106
+ -
107
+ + # If current radii would cause overlap
108
+ + if radii[i] + radii[j] > dist:
109
+ + # Scale both radii proportionally to just touch
110
+ + scale = dist / (radii[i] + radii[j])
111
+ + radii[i] *= scale
112
+ + radii[j] *= scale
113
+ return radii
114
+
115
+
116
+ # EVOLVE-BLOCK-END
117
+
118
+
119
+ # This part remains fixed (not evolved)
120
+ def run_packing():
121
+ """Run the circle packing constructor for n=26"""
122
+ centers, radii = construct_packing()
123
+ # Calculate the sum of radii
124
+ sum_radii = np.sum(radii)
125
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/main.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """Constructor-based circle packing for n=26 circles"""
3
+
4
+ import numpy as np
5
+
6
+
7
+ def construct_packing():
8
+ """
9
+ Construct a specific arrangement of 26 circles in a unit square
10
+ that attempts to maximize the sum of their radii.
11
+
12
+ Returns:
13
+ Tuple of (centers, radii, sum_of_radii)
14
+ centers: np.array of shape (26, 2) with (x, y) coordinates
15
+ radii: np.array of shape (26) with radius of each circle
16
+ sum_of_radii: Sum of all radii
17
+ """
18
+ # Initialize arrays for 26 circles
19
+ n = 26
20
+ centers = np.zeros((n, 2))
21
+
22
+ # Place 25 circles in a 5x5 grid
23
+ # This provides a more even distribution and better use of space
24
+ num_cells_side = 5
25
+ spacing = 1.0 / num_cells_side
26
+
27
+ k = 0
28
+ for i in range(num_cells_side):
29
+ for j in range(num_cells_side):
30
+ centers[k, 0] = (i + 0.5) * spacing
31
+ centers[k, 1] = (j + 0.5) * spacing
32
+ k += 1
33
+
34
+ # Place the 26th circle in a corner, slightly offset to allow for radius.
35
+ # This location is chosen to utilize corner space efficiently,
36
+ # as the grid circles are not directly in the corners.
37
+ centers[25] = [0.05, 0.05]
38
+
39
+ # No need to clip centers, as they are intentionally placed within (0,1) range
40
+ # and compute_max_radii handles boundary constraints.
41
+
42
+ # Compute maximum valid radii for this configuration
43
+ radii = compute_max_radii(centers)
44
+ return centers, radii
45
+
46
+
47
+ def compute_max_radii(centers):
48
+ """
49
+ Compute the maximum possible radii for each circle position
50
+ such that they don't overlap and stay within the unit square.
51
+
52
+ Args:
53
+ centers: np.array of shape (n, 2) with (x, y) coordinates
54
+
55
+ Returns:
56
+ np.array of shape (n) with radius of each circle
57
+ """
58
+ n = centers.shape[0]
59
+ radii = np.ones(n)
60
+
61
+ # First, limit by distance to square borders
62
+ for i in range(n):
63
+ x, y = centers[i]
64
+ # Distance to borders
65
+ radii[i] = min(x, y, 1 - x, 1 - y)
66
+
67
+ # Then, limit by distance to other circles
68
+ # Each pair of circles with centers at distance d can have
69
+ # sum of radii at most d to avoid overlap
70
+ # This iterative scaling is a greedy approach and the order can matter,
71
+ # but for constructor-based approaches, it's a common method.
72
+ for _ in range(100): # Iterate multiple times to stabilize radii
73
+ for i in range(n):
74
+ for j in range(i + 1, n):
75
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
76
+
77
+ # If current radii would cause overlap
78
+ if radii[i] + radii[j] > dist:
79
+ # Scale both radii proportionally to just touch
80
+ scale = dist / (radii[i] + radii[j])
81
+ radii[i] *= scale
82
+ radii[j] *= scale
83
+ return radii
84
+
85
+
86
+ # EVOLVE-BLOCK-END
87
+
88
+
89
+ # This part remains fixed (not evolved)
90
+ def run_packing():
91
+ """Run the circle packing constructor for n=26"""
92
+ centers, radii = construct_packing()
93
+ # Calculate the sum of radii
94
+ sum_radii = np.sum(radii)
95
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/original.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """Constructor-based circle packing for n=26 circles"""
3
+
4
+ import numpy as np
5
+
6
+
7
+ def construct_packing():
8
+ """
9
+ Construct a specific arrangement of 26 circles in a unit square
10
+ that attempts to maximize the sum of their radii.
11
+
12
+ Returns:
13
+ Tuple of (centers, radii, sum_of_radii)
14
+ centers: np.array of shape (26, 2) with (x, y) coordinates
15
+ radii: np.array of shape (26) with radius of each circle
16
+ sum_of_radii: Sum of all radii
17
+ """
18
+ # Initialize arrays for 26 circles
19
+ n = 26
20
+ centers = np.zeros((n, 2))
21
+
22
+ # Place circles in a structured pattern
23
+ # This is a simple pattern - evolution will improve this
24
+
25
+ # First, place a large circle in the center
26
+ centers[0] = [0.5, 0.5]
27
+
28
+ # Place 8 circles around it in a ring
29
+ for i in range(8):
30
+ angle = 2 * np.pi * i / 8
31
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
32
+
33
+ # Place 16 more circles in an outer ring
34
+ for i in range(16):
35
+ angle = 2 * np.pi * i / 16
36
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
37
+
38
+ # Additional positioning adjustment to make sure all circles
39
+ # are inside the square and don't overlap
40
+ # Clip to ensure everything is inside the unit square
41
+ centers = np.clip(centers, 0.01, 0.99)
42
+
43
+ # Compute maximum valid radii for this configuration
44
+ radii = compute_max_radii(centers)
45
+ return centers, radii
46
+
47
+
48
+ def compute_max_radii(centers):
49
+ """
50
+ Compute the maximum possible radii for each circle position
51
+ such that they don't overlap and stay within the unit square.
52
+
53
+ Args:
54
+ centers: np.array of shape (n, 2) with (x, y) coordinates
55
+
56
+ Returns:
57
+ np.array of shape (n) with radius of each circle
58
+ """
59
+ n = centers.shape[0]
60
+ radii = np.ones(n)
61
+
62
+ # First, limit by distance to square borders
63
+ for i in range(n):
64
+ x, y = centers[i]
65
+ # Distance to borders
66
+ radii[i] = min(x, y, 1 - x, 1 - y)
67
+
68
+ # Then, limit by distance to other circles
69
+ # Each pair of circles with centers at distance d can have
70
+ # sum of radii at most d to avoid overlap
71
+ for i in range(n):
72
+ for j in range(i + 1, n):
73
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
74
+
75
+ # If current radii would cause overlap
76
+ if radii[i] + radii[j] > dist:
77
+ # Scale both radii proportionally
78
+ scale = dist / (radii[i] + radii[j])
79
+ radii[i] *= scale
80
+ radii[j] *= scale
81
+
82
+ return radii
83
+
84
+
85
+ # EVOLVE-BLOCK-END
86
+
87
+
88
+ # This part remains fixed (not evolved)
89
+ def run_packing():
90
+ """Run the circle packing constructor for n=26"""
91
+ centers, radii = construct_packing()
92
+ # Calculate the sum of radii
93
+ sum_radii = np.sum(radii)
94
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/correct.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "correct": true,
3
+ "error": null
4
+ }
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/job_log.err ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
2
+
3
+ All support for the `google.generativeai` package has ended. It will no longer be receiving
4
+ updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
5
+ See README for more details:
6
+
7
+ https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
8
+
9
+ import google.generativeai as genai # type: ignore[import-not-found]
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/job_log.out ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/main.py
2
+ Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results
3
+ Run 1/1 completed in 0.17 seconds
4
+ Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/extra.npz
5
+ Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/packing_viz.png
6
+ Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/correct.json
7
+ Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/metrics.json
8
+ Evaluation and Validation completed successfully.
9
+ Metrics:
10
+ combined_score: 1.8730177484885533
11
+ 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}
12
+ private: {'reported_sum_of_radii': 1.8730177484885533}
13
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/packing_viz.png
14
+ execution_time_mean: 0.17472516791895032
15
+ execution_time_std: 0.0
16
+ num_valid_runs: 1
17
+ num_invalid_runs: 0
18
+ all_validation_errors: []
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/metrics.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "combined_score": 1.8730177484885533,
3
+ "public": {
4
+ "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)",
5
+ "num_circles": 26
6
+ },
7
+ "private": {
8
+ "reported_sum_of_radii": 1.8730177484885533
9
+ },
10
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/results/packing_viz.png",
11
+ "execution_time_mean": 0.17472516791895032,
12
+ "execution_time_std": 0.0,
13
+ "num_valid_runs": 1,
14
+ "num_invalid_runs": 0,
15
+ "all_validation_errors": []
16
+ }
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_1/search_replace.txt ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <first_major_refactor>
2
+ <DESCRIPTION>
3
+ 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.
4
+
5
+ This edit proposes two main improvements:
6
+
7
+ 1. **Refactor `construct_packing` for better spatial distribution:**
8
+ * 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`).
9
+ * 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]`).
10
+ * 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.
11
+
12
+ 2. **Improve `compute_max_radii` for better stabilization:**
13
+ * 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.
14
+ * 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.
15
+
16
+ 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.
17
+ </DESCRIPTION>
18
+ <DIFF>
19
+ <<<<<<< SEARCH
20
+ """Constructor-based circle packing for n=26 circles"""
21
+
22
+ import numpy as np
23
+
24
+
25
+ def construct_packing():
26
+ """
27
+ Construct a specific arrangement of 26 circles in a unit square
28
+ that attempts to maximize the sum of their radii.
29
+
30
+ Returns:
31
+ Tuple of (centers, radii, sum_of_radii)
32
+ centers: np.array of shape (26, 2) with (x, y) coordinates
33
+ radii: np.array of shape (26) with radius of each circle
34
+ sum_of_radii: Sum of all radii
35
+ """
36
+ # Initialize arrays for 26 circles
37
+ n = 26
38
+ centers = np.zeros((n, 2))
39
+
40
+ # Place circles in a structured pattern
41
+ # This is a simple pattern - evolution will improve this
42
+
43
+ # First, place a large circle in the center
44
+ centers[0] = [0.5, 0.5]
45
+
46
+ # Place 8 circles around it in a ring
47
+ for i in range(8):
48
+ angle = 2 * np.pi * i / 8
49
+ centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
50
+
51
+ # Place 16 more circles in an outer ring
52
+ for i in range(16):
53
+ angle = 2 * np.pi * i / 16
54
+ centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
55
+
56
+ # Additional positioning adjustment to make sure all circles
57
+ # are inside the square and don't overlap
58
+ # Clip to ensure everything is inside the unit square
59
+ centers = np.clip(centers, 0.01, 0.99)
60
+
61
+ # Compute maximum valid radii for this configuration
62
+ radii = compute_max_radii(centers)
63
+ return centers, radii
64
+
65
+
66
+ def compute_max_radii(centers):
67
+ """
68
+ Compute the maximum possible radii for each circle position
69
+ such that they don't overlap and stay within the unit square.
70
+
71
+ Args:
72
+ centers: np.array of shape (n, 2) with (x, y) coordinates
73
+
74
+ Returns:
75
+ np.array of shape (n) with radius of each circle
76
+ """
77
+ n = centers.shape[0]
78
+ radii = np.ones(n)
79
+
80
+ # First, limit by distance to square borders
81
+ for i in range(n):
82
+ x, y = centers[i]
83
+ # Distance to borders
84
+ radii[i] = min(x, y, 1 - x, 1 - y)
85
+
86
+ # Then, limit by distance to other circles
87
+ # Each pair of circles with centers at distance d can have
88
+ # sum of radii at most d to avoid overlap
89
+ for i in range(n):
90
+ for j in range(i + 1, n):
91
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
92
+
93
+ # If current radii would cause overlap
94
+ if radii[i] + radii[j] > dist:
95
+ # Scale both radii proportionally
96
+ scale = dist / (radii[i] + radii[j])
97
+ radii[i] *= scale
98
+ radii[j] *= scale
99
+
100
+ return radii
101
+ =======
102
+ """Constructor-based circle packing for n=26 circles"""
103
+
104
+ import numpy as np
105
+
106
+
107
+ def construct_packing():
108
+ """
109
+ Construct a specific arrangement of 26 circles in a unit square
110
+ that attempts to maximize the sum of their radii.
111
+
112
+ Returns:
113
+ Tuple of (centers, radii, sum_of_radii)
114
+ centers: np.array of shape (26, 2) with (x, y) coordinates
115
+ radii: np.array of shape (26) with radius of each circle
116
+ sum_of_radii: Sum of all radii
117
+ """
118
+ # Initialize arrays for 26 circles
119
+ n = 26
120
+ centers = np.zeros((n, 2))
121
+
122
+ # Place 25 circles in a 5x5 grid
123
+ # This provides a more even distribution and better use of space
124
+ num_cells_side = 5
125
+ spacing = 1.0 / num_cells_side
126
+
127
+ k = 0
128
+ for i in range(num_cells_side):
129
+ for j in range(num_cells_side):
130
+ centers[k, 0] = (i + 0.5) * spacing
131
+ centers[k, 1] = (j + 0.5) * spacing
132
+ k += 1
133
+
134
+ # Place the 26th circle in a corner, slightly offset to allow for radius.
135
+ # This location is chosen to utilize corner space efficiently,
136
+ # as the grid circles are not directly in the corners.
137
+ centers[25] = [0.05, 0.05]
138
+
139
+ # No need to clip centers, as they are intentionally placed within (0,1) range
140
+ # and compute_max_radii handles boundary constraints.
141
+
142
+ # Compute maximum valid radii for this configuration
143
+ radii = compute_max_radii(centers)
144
+ return centers, radii
145
+
146
+
147
+ def compute_max_radii(centers):
148
+ """
149
+ Compute the maximum possible radii for each circle position
150
+ such that they don't overlap and stay within the unit square.
151
+
152
+ Args:
153
+ centers: np.array of shape (n, 2) with (x, y) coordinates
154
+
155
+ Returns:
156
+ np.array of shape (n) with radius of each circle
157
+ """
158
+ n = centers.shape[0]
159
+ radii = np.ones(n)
160
+
161
+ # First, limit by distance to square borders
162
+ for i in range(n):
163
+ x, y = centers[i]
164
+ # Distance to borders
165
+ radii[i] = min(x, y, 1 - x, 1 - y)
166
+
167
+ # Then, limit by distance to other circles
168
+ # Each pair of circles with centers at distance d can have
169
+ # sum of radii at most d to avoid overlap
170
+ # This iterative scaling is a greedy approach and the order can matter,
171
+ # but for constructor-based approaches, it's a common method.
172
+ for _ in range(100): # Iterate multiple times to stabilize radii
173
+ for i in range(n):
174
+ for j in range(i + 1, n):
175
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
176
+
177
+ # If current radii would cause overlap
178
+ if radii[i] + radii[j] > dist:
179
+ # Scale both radii proportionally to just touch
180
+ scale = dist / (radii[i] + radii[j])
181
+ radii[i] *= scale
182
+ radii[j] *= scale
183
+ return radii
184
+ >>>>>>> REPLACE
185
+ </DIFF>
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/__pycache__/main.cpython-311.pyc ADDED
Binary file (4.84 kB). View file
 
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/edit.diff ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,128 +1,124 @@
4
+ # EVOLVE-BLOCK-START
5
+ """
6
+ Hybrid circle packing for n=26 circles.
7
+ Starts with a concentric ring pattern and refines positions using
8
+ force-directed relaxation, then computes radii iteratively.
9
+ """
10
+
11
+ import numpy as np
12
+
13
+
14
+ def construct_packing():
15
+ """
16
+ Constructs an arrangement of 26 circles using a force-directed
17
+ relaxation method, starting from a concentric ring pattern,
18
+ to maximize the sum of radii.
19
+
20
+ Returns:
21
+ Tuple of (centers, radii)
22
+ centers: np.array of shape (26, 2) with (x, y) coordinates
23
+ radii: np.array of shape (26) with radius of each circle
24
+ """
25
+ n = 26
26
+ # Use a fixed seed for reproducibility.
27
+ np.random.seed(1337)
28
+
29
+ - # 1. Initial Placement: Start with a structured concentric ring pattern.
30
+ - # This provides a symmetric, ordered starting point for the relaxation,
31
+ - # combining the idea from the first parent program.
32
+ + # 1. Initial Placement: Start with a structured near-grid pattern (5-5-6-5-5 columns).
33
+ + # This is a much better starting point than concentric circles, inspired by known optimal packings.
34
+ centers = np.zeros((n, 2))
35
+ + col_counts = [5, 5, 6, 5, 5]
36
+ + x_coords = np.linspace(0.1, 0.9, 5)
37
+ + current_idx = 0
38
+ + for i, count in enumerate(col_counts):
39
+ + y_coords = np.linspace(1/(count+1), count/(count+1), count)
40
+ + for j in range(count):
41
+ + centers[current_idx] = [x_coords[i], y_coords[j]]
42
+ + current_idx += 1
43
+
44
+ - # Place a circle in the center
45
+ - centers[0] = [0.5, 0.5]
46
+ -
47
+ - # Place 8 circles in an inner ring
48
+ - inner_ring_radius = 0.28
49
+ - for i in range(8):
50
+ - angle = 2 * np.pi * i / 8
51
+ - centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
52
+ -
53
+ - # Place the remaining 17 circles in an outer ring
54
+ - outer_ring_radius = 0.48
55
+ - for i in range(17):
56
+ - angle = 2 * np.pi * i / 17
57
+ - centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
58
+ + # Add random jitter to break perfect symmetry and allow for new configurations.
59
+ + centers += np.random.uniform(-0.02, 0.02, size=centers.shape)
60
+ + centers = np.clip(centers, 0.01, 0.99) # Ensure they start safely inside.
61
+
62
+ # 2. Iterative Position Refinement (Force Simulation from the second parent)
63
+ - n_iter = 250
64
+ + n_iter = 500 # Increased iterations for better convergence
65
+ step_size = 1e-4
66
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
67
+
68
+ for _ in range(n_iter):
69
+ # Vectorized calculation of repulsion forces between all pairs of circles
70
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
71
+ dist_sq = np.sum(diffs**2, axis=-1)
72
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
73
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
74
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
75
+ force_repel = np.sum(force_repel_matrix, axis=1)
76
+
77
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
78
+ # This creates a soft potential well keeping circles inside the square.
79
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
80
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
81
+
82
+ # Combine forces and update positions
83
+ total_force = force_repel + wall_strength * force_wall
84
+ centers += step_size * total_force
85
+
86
+ # Enforce hard boundaries
87
+ centers = np.clip(centers, 0.0, 1.0)
88
+
89
+ # 3. Compute optimal radii for the final configuration using the convergent method.
90
+ radii = compute_max_radii(centers)
91
+
92
+ return centers, radii
93
+
94
+
95
+ def compute_max_radii(centers):
96
+ """
97
+ Compute the maximum possible radii for the given centers using an
98
+ iterative relaxation method until convergence. This is more robust
99
+ than the original single-pass approach.
100
+ """
101
+ n = centers.shape[0]
102
+
103
+ # Initialize radii based on the distance to the four walls.
104
+ radii = np.min([
105
+ centers[:, 0],
106
+ centers[:, 1],
107
+ 1 - centers[:, 0],
108
+ 1 - centers[:, 1]
109
+ ], axis=0)
110
+
111
+ # Iteratively shrink any overlapping circles until the layout is stable.
112
+ max_iter = 500 # Safety break for the convergence loop
113
+ for _ in range(max_iter):
114
+ changed_in_iter = False
115
+ # Check every pair of circles for overlap
116
+ for i in range(n):
117
+ for j in range(i + 1, n):
118
+ dist = np.linalg.norm(centers[i] - centers[j])
119
+ sum_r = radii[i] + radii[j]
120
+
121
+ if sum_r > dist:
122
+ # Overlap detected. Shrink radii proportionally to their size.
123
+ overlap = sum_r - dist
124
+ if sum_r > 1e-9: # Avoid division by zero
125
+ radii[i] -= overlap * (radii[i] / sum_r)
126
+ radii[j] -= overlap * (radii[j] / sum_r)
127
+ changed_in_iter = True
128
+
129
+ if not changed_in_iter:
130
+ # If a full pass completes with no changes, the radii have converged.
131
+ break
132
+
133
+ return radii
134
+
135
+
136
+ # EVOLVE-BLOCK-END
137
+
138
+
139
+ # This part remains fixed (not evolved)
140
+ def run_packing():
141
+ """Run the circle packing constructor for n=26"""
142
+ centers, radii = construct_packing()
143
+ # Calculate the sum of radii
144
+ sum_radii = np.sum(radii)
145
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/main.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Hybrid circle packing for n=26 circles.
4
+ Starts with a concentric ring pattern and refines positions using
5
+ force-directed relaxation, then computes radii iteratively.
6
+ """
7
+
8
+ import numpy as np
9
+
10
+
11
+ def construct_packing():
12
+ """
13
+ Constructs an arrangement of 26 circles using a force-directed
14
+ relaxation method, starting from a concentric ring pattern,
15
+ to maximize the sum of radii.
16
+
17
+ Returns:
18
+ Tuple of (centers, radii)
19
+ centers: np.array of shape (26, 2) with (x, y) coordinates
20
+ radii: np.array of shape (26) with radius of each circle
21
+ """
22
+ n = 26
23
+ # Use a fixed seed for reproducibility.
24
+ np.random.seed(1337)
25
+
26
+ # 1. Initial Placement: Start with a structured near-grid pattern (5-5-6-5-5 columns).
27
+ # This is a much better starting point than concentric circles, inspired by known optimal packings.
28
+ centers = np.zeros((n, 2))
29
+ col_counts = [5, 5, 6, 5, 5]
30
+ x_coords = np.linspace(0.1, 0.9, 5)
31
+ current_idx = 0
32
+ for i, count in enumerate(col_counts):
33
+ y_coords = np.linspace(1/(count+1), count/(count+1), count)
34
+ for j in range(count):
35
+ centers[current_idx] = [x_coords[i], y_coords[j]]
36
+ current_idx += 1
37
+
38
+ # Add random jitter to break perfect symmetry and allow for new configurations.
39
+ centers += np.random.uniform(-0.02, 0.02, size=centers.shape)
40
+ centers = np.clip(centers, 0.01, 0.99) # Ensure they start safely inside.
41
+
42
+ # 2. Iterative Position Refinement (Force Simulation from the second parent)
43
+ n_iter = 500 # Increased iterations for better convergence
44
+ step_size = 1e-4
45
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
46
+
47
+ for _ in range(n_iter):
48
+ # Vectorized calculation of repulsion forces between all pairs of circles
49
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
50
+ dist_sq = np.sum(diffs**2, axis=-1)
51
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
52
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
53
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
54
+ force_repel = np.sum(force_repel_matrix, axis=1)
55
+
56
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
57
+ # This creates a soft potential well keeping circles inside the square.
58
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
59
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
60
+
61
+ # Combine forces and update positions
62
+ total_force = force_repel + wall_strength * force_wall
63
+ centers += step_size * total_force
64
+
65
+ # Enforce hard boundaries
66
+ centers = np.clip(centers, 0.0, 1.0)
67
+
68
+ # 3. Compute optimal radii for the final configuration using the convergent method.
69
+ radii = compute_max_radii(centers)
70
+
71
+ return centers, radii
72
+
73
+
74
+ def compute_max_radii(centers):
75
+ """
76
+ Compute the maximum possible radii for the given centers using an
77
+ iterative relaxation method until convergence. This is more robust
78
+ than the original single-pass approach.
79
+ """
80
+ n = centers.shape[0]
81
+
82
+ # Initialize radii based on the distance to the four walls.
83
+ radii = np.min([
84
+ centers[:, 0],
85
+ centers[:, 1],
86
+ 1 - centers[:, 0],
87
+ 1 - centers[:, 1]
88
+ ], axis=0)
89
+
90
+ # Iteratively shrink any overlapping circles until the layout is stable.
91
+ max_iter = 500 # Safety break for the convergence loop
92
+ for _ in range(max_iter):
93
+ changed_in_iter = False
94
+ # Check every pair of circles for overlap
95
+ for i in range(n):
96
+ for j in range(i + 1, n):
97
+ dist = np.linalg.norm(centers[i] - centers[j])
98
+ sum_r = radii[i] + radii[j]
99
+
100
+ if sum_r > dist:
101
+ # Overlap detected. Shrink radii proportionally to their size.
102
+ overlap = sum_r - dist
103
+ if sum_r > 1e-9: # Avoid division by zero
104
+ radii[i] -= overlap * (radii[i] / sum_r)
105
+ radii[j] -= overlap * (radii[j] / sum_r)
106
+ changed_in_iter = True
107
+
108
+ if not changed_in_iter:
109
+ # If a full pass completes with no changes, the radii have converged.
110
+ break
111
+
112
+ return radii
113
+
114
+
115
+ # EVOLVE-BLOCK-END
116
+
117
+
118
+ # This part remains fixed (not evolved)
119
+ def run_packing():
120
+ """Run the circle packing constructor for n=26"""
121
+ centers, radii = construct_packing()
122
+ # Calculate the sum of radii
123
+ sum_radii = np.sum(radii)
124
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/original.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Hybrid circle packing for n=26 circles.
4
+ Starts with a concentric ring pattern and refines positions using
5
+ force-directed relaxation, then computes radii iteratively.
6
+ """
7
+
8
+ import numpy as np
9
+
10
+
11
+ def construct_packing():
12
+ """
13
+ Constructs an arrangement of 26 circles using a force-directed
14
+ relaxation method, starting from a concentric ring pattern,
15
+ to maximize the sum of radii.
16
+
17
+ Returns:
18
+ Tuple of (centers, radii)
19
+ centers: np.array of shape (26, 2) with (x, y) coordinates
20
+ radii: np.array of shape (26) with radius of each circle
21
+ """
22
+ n = 26
23
+ # Use a fixed seed for reproducibility.
24
+ np.random.seed(1337)
25
+
26
+ # 1. Initial Placement: Start with a structured concentric ring pattern.
27
+ # This provides a symmetric, ordered starting point for the relaxation,
28
+ # combining the idea from the first parent program.
29
+ centers = np.zeros((n, 2))
30
+
31
+ # Place a circle in the center
32
+ centers[0] = [0.5, 0.5]
33
+
34
+ # Place 8 circles in an inner ring
35
+ inner_ring_radius = 0.28
36
+ for i in range(8):
37
+ angle = 2 * np.pi * i / 8
38
+ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
39
+
40
+ # Place the remaining 17 circles in an outer ring
41
+ outer_ring_radius = 0.48
42
+ for i in range(17):
43
+ angle = 2 * np.pi * i / 17
44
+ centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
45
+
46
+ # 2. Iterative Position Refinement (Force Simulation from the second parent)
47
+ n_iter = 250
48
+ step_size = 1e-4
49
+ wall_strength = 0.5 # Relative strength of wall vs particle repulsion
50
+
51
+ for _ in range(n_iter):
52
+ # Vectorized calculation of repulsion forces between all pairs of circles
53
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
54
+ dist_sq = np.sum(diffs**2, axis=-1)
55
+ np.fill_diagonal(dist_sq, 1.0) # Avoid self-repulsion and division by zero
56
+ inv_dist_sq = 1.0 / (dist_sq + 1e-8)
57
+ force_repel_matrix = diffs * inv_dist_sq[:, :, np.newaxis]
58
+ force_repel = np.sum(force_repel_matrix, axis=1)
59
+
60
+ # Wall repulsion force: F_wall is proportional to 1/d - 1/(1-d)
61
+ # This creates a soft potential well keeping circles inside the square.
62
+ clipped_centers = np.clip(centers, 1e-6, 1 - 1e-6)
63
+ force_wall = 1.0 / clipped_centers - 1.0 / (1.0 - clipped_centers)
64
+
65
+ # Combine forces and update positions
66
+ total_force = force_repel + wall_strength * force_wall
67
+ centers += step_size * total_force
68
+
69
+ # Enforce hard boundaries
70
+ centers = np.clip(centers, 0.0, 1.0)
71
+
72
+ # 3. Compute optimal radii for the final configuration using the convergent method.
73
+ radii = compute_max_radii(centers)
74
+
75
+ return centers, radii
76
+
77
+
78
+ def compute_max_radii(centers):
79
+ """
80
+ Compute the maximum possible radii for the given centers using an
81
+ iterative relaxation method until convergence. This is more robust
82
+ than the original single-pass approach.
83
+ """
84
+ n = centers.shape[0]
85
+
86
+ # Initialize radii based on the distance to the four walls.
87
+ radii = np.min([
88
+ centers[:, 0],
89
+ centers[:, 1],
90
+ 1 - centers[:, 0],
91
+ 1 - centers[:, 1]
92
+ ], axis=0)
93
+
94
+ # Iteratively shrink any overlapping circles until the layout is stable.
95
+ max_iter = 500 # Safety break for the convergence loop
96
+ for _ in range(max_iter):
97
+ changed_in_iter = False
98
+ # Check every pair of circles for overlap
99
+ for i in range(n):
100
+ for j in range(i + 1, n):
101
+ dist = np.linalg.norm(centers[i] - centers[j])
102
+ sum_r = radii[i] + radii[j]
103
+
104
+ if sum_r > dist:
105
+ # Overlap detected. Shrink radii proportionally to their size.
106
+ overlap = sum_r - dist
107
+ if sum_r > 1e-9: # Avoid division by zero
108
+ radii[i] -= overlap * (radii[i] / sum_r)
109
+ radii[j] -= overlap * (radii[j] / sum_r)
110
+ changed_in_iter = True
111
+
112
+ if not changed_in_iter:
113
+ # If a full pass completes with no changes, the radii have converged.
114
+ break
115
+
116
+ return radii
117
+
118
+
119
+ # EVOLVE-BLOCK-END
120
+
121
+
122
+ # This part remains fixed (not evolved)
123
+ def run_packing():
124
+ """Run the circle packing constructor for n=26"""
125
+ centers, radii = construct_packing()
126
+ # Calculate the sum of radii
127
+ sum_radii = np.sum(radii)
128
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/correct.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "correct": true,
3
+ "error": null
4
+ }
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/job_log.err ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
2
+
3
+ All support for the `google.generativeai` package has ended. It will no longer be receiving
4
+ updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
5
+ See README for more details:
6
+
7
+ https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
8
+
9
+ import google.generativeai as genai # type: ignore[import-not-found]
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/job_log.out ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/main.py
2
+ Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results
3
+ Run 1/1 completed in 0.04 seconds
4
+ Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/extra.npz
5
+ Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/packing_viz.png
6
+ Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/correct.json
7
+ Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/metrics.json
8
+ Evaluation and Validation completed successfully.
9
+ Metrics:
10
+ combined_score: 1.0573677714897678
11
+ 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}
12
+ private: {'reported_sum_of_radii': 1.0573677714897678}
13
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/packing_viz.png
14
+ execution_time_mean: 0.04305929783731699
15
+ execution_time_std: 0.0
16
+ num_valid_runs: 1
17
+ num_invalid_runs: 0
18
+ all_validation_errors: []
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/metrics.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "combined_score": 1.0573677714897678,
3
+ "public": {
4
+ "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)",
5
+ "num_circles": 26
6
+ },
7
+ "private": {
8
+ "reported_sum_of_radii": 1.0573677714897678
9
+ },
10
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/results/packing_viz.png",
11
+ "execution_time_mean": 0.04305929783731699,
12
+ "execution_time_std": 0.0,
13
+ "num_valid_runs": 1,
14
+ "num_invalid_runs": 0,
15
+ "all_validation_errors": []
16
+ }
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_10/search_replace.txt ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <NAME>
2
+ grid_based_initialization
3
+ </NAME>
4
+ <DESCRIPTION>
5
+ 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.
6
+
7
+ 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.
8
+ </DESCRIPTION>
9
+ <DIFF>
10
+ <<<<<<< SEARCH
11
+ # 1. Initial Placement: Start with a structured concentric ring pattern.
12
+ # This provides a symmetric, ordered starting point for the relaxation,
13
+ # combining the idea from the first parent program.
14
+ centers = np.zeros((n, 2))
15
+
16
+ # Place a circle in the center
17
+ centers[0] = [0.5, 0.5]
18
+
19
+ # Place 8 circles in an inner ring
20
+ inner_ring_radius = 0.28
21
+ for i in range(8):
22
+ angle = 2 * np.pi * i / 8
23
+ centers[i + 1] = [0.5 + inner_ring_radius * np.cos(angle), 0.5 + inner_ring_radius * np.sin(angle)]
24
+
25
+ # Place the remaining 17 circles in an outer ring
26
+ outer_ring_radius = 0.48
27
+ for i in range(17):
28
+ angle = 2 * np.pi * i / 17
29
+ centers[i + 9] = [0.5 + outer_ring_radius * np.cos(angle), 0.5 + outer_ring_radius * np.sin(angle)]
30
+ =======
31
+ # 1. Initial Placement: Start with a structured near-grid pattern (5-5-6-5-5 columns).
32
+ # This is a much better starting point than concentric circles, inspired by known optimal packings.
33
+ centers = np.zeros((n, 2))
34
+ col_counts = [5, 5, 6, 5, 5]
35
+ x_coords = np.linspace(0.1, 0.9, 5)
36
+ current_idx = 0
37
+ for i, count in enumerate(col_counts):
38
+ y_coords = np.linspace(1/(count+1), count/(count+1), count)
39
+ for j in range(count):
40
+ centers[current_idx] = [x_coords[i], y_coords[j]]
41
+ current_idx += 1
42
+
43
+ # Add random jitter to break perfect symmetry and allow for new configurations.
44
+ centers += np.random.uniform(-0.02, 0.02, size=centers.shape)
45
+ centers = np.clip(centers, 0.01, 0.99) # Ensure they start safely inside.
46
+ >>>>>>> REPLACE
47
+ </DIFF>
48
+ <NAME>
49
+ increase_relaxation_iterations
50
+ </NAME>
51
+ <DESCRIPTION>
52
+ 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.
53
+ </DESCRIPTION>
54
+ <DIFF>
55
+ <<<<<<< SEARCH
56
+ n_iter = 250
57
+ =======
58
+ n_iter = 500 # Increased iterations for better convergence
59
+ >>>>>>> REPLACE
60
+ </DIFF>
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/__pycache__/main.cpython-311.pyc ADDED
Binary file (14.2 kB). View file
 
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/edit.diff ADDED
@@ -0,0 +1,508 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,306 +1,250 @@
4
+ # EVOLVE-BLOCK-START
5
+ -"""
6
+ -This solution implements a Memetic Algorithm (MA), which combines a Genetic
7
+ -Algorithm (GA) for global exploration with a powerful, physics-based local search
8
+ -for exploitation. This approach hybridizes the successful population-based
9
+ -framework of prior submissions with the refined, two-stage force-directed
10
+ -simulation from the current program, which will be repurposed as the local
11
+ -search operator.
12
+ -"""
13
+ -
14
+ import numpy as np
15
+ -
16
+ -class LocalSearchRefiner:
17
+ - """
18
+ - Performs a fast, two-stage, force-directed local refinement on a set of centers.
19
+ - This is adapted from the full simulation of the parent program to act as a
20
+ - powerful local search operator within the Memetic Algorithm.
21
+ +import math
22
+ +import random
23
+ +
24
+ +def compute_max_radii(centers, max_iter=800, convergence_threshold=1e-8):
25
+ + """
26
+ + Compute maximum radii using iterative proportional scaling.
27
+ + This is a robust version with a stable convergence check.
28
+ + """
29
+ + n = centers.shape[0]
30
+ + if n == 0:
31
+ + return np.array([])
32
+ +
33
+ + radii = np.min([
34
+ + centers[:, 0], 1 - centers[:, 0],
35
+ + centers[:, 1], 1 - centers[:, 1]
36
+ + ], axis=0)
37
+ +
38
+ + required_converged_iters = 3
39
+ + converged_in_a_row = 0
40
+ +
41
+ + for _ in range(max_iter):
42
+ + old_radii = radii.copy()
43
+ +
44
+ + for i in range(n):
45
+ + for j in range(i + 1, n):
46
+ + dist_sq = np.sum((centers[i] - centers[j])**2)
47
+ + dist = np.sqrt(dist_sq)
48
+ +
49
+ + if radii[i] + radii[j] > dist:
50
+ + if dist < 1e-9:
51
+ + radii[i], radii[j] = 0.0, 0.0
52
+ + else:
53
+ + scale = dist / (radii[i] + radii[j])
54
+ + radii[i] *= scale
55
+ + radii[j] *= scale
56
+ +
57
+ + max_abs_change = np.max(np.abs(radii - old_radii))
58
+ + if max_abs_change < convergence_threshold:
59
+ + converged_in_a_row += 1
60
+ + if converged_in_a_row >= required_converged_iters:
61
+ + break
62
+ + else:
63
+ + converged_in_a_row = 0
64
+ +
65
+ + return np.maximum(radii, 0)
66
+ +
67
+ +
68
+ +class QuickRefiner:
69
+ + """
70
+ + A fast, aggressive, single-phase local search operator for memetic injection.
71
+ """
72
+ def __init__(self, config):
73
+ self.config = config
74
+
75
+ def refine(self, centers):
76
+ - """Applies a two-stage force-directed refinement to polish a solution."""
77
+ refined_centers = centers.copy()
78
+ - n = refined_centers.shape[0]
79
+ -
80
+ - # --- Stage 1: Aggressive "unfreezing" and resettlement ---
81
+ - for sim_iter in range(self.config['lsr_aggressive_iter']):
82
+ - progress = sim_iter / self.config['lsr_aggressive_iter']
83
+ - current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
84
+ - current_pressure = self.config['lsr_aggressive_pressure_end'] + \
85
+ - (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
86
+ -
87
+ - radii = compute_max_radii(refined_centers)
88
+ +
89
+ + for sim_iter in range(self.config['refiner_iter']):
90
+ + progress = sim_iter / self.config['refiner_iter']
91
+ + current_lr = self.config['refiner_lr'] * (1.0 - progress)**2
92
+ + current_pressure = self.config['refiner_pressure_end'] + \
93
+ + (self.config['refiner_pressure_start'] - self.config['refiner_pressure_end']) * (1.0 - progress)**2
94
+ +
95
+ + # Fast radius calculation for simulation
96
+ + radii = compute_max_radii(refined_centers, max_iter=100)
97
+ inflated_radii = radii * current_pressure
98
+
99
+ + # Vectorized force calculations
100
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
101
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
102
+ + dists[dists < 1e-9] = 1e-9
103
+
104
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
105
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
106
+ np.fill_diagonal(overlaps, 0)
107
+ -
108
+ - with np.errstate(divide='ignore', invalid='ignore'):
109
+ - unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
110
+ - unit_vectors[dists < 1e-9] = 0
111
+ -
112
+ - circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
113
+ +
114
+ + force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
115
+ + circle_forces = np.sum(force_matrix, axis=1)
116
+
117
+ wall_forces = np.zeros_like(refined_centers)
118
+ - wall_strength = self.config['lsr_wall_strength']
119
+ + wall_strength = self.config['refiner_wall_strength']
120
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
121
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
122
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
123
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
124
+
125
+ forces = circle_forces + wall_forces
126
+ refined_centers += forces * current_lr
127
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
128
+ -
129
+ - # --- Stage 2: Fine-tuning with subtle growth pressure ---
130
+ - for sim_iter_ft in range(self.config['lsr_finetune_iter']):
131
+ - progress = sim_iter_ft / self.config['lsr_finetune_iter']
132
+ - current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
133
+ - current_pressure = self.config['lsr_finetune_pressure_end'] + \
134
+ - (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
135
+ -
136
+ - radii = compute_max_radii(refined_centers)
137
+ - inflated_radii = radii * current_pressure
138
+ -
139
+ - diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
140
+ - dists = np.sqrt(np.sum(diffs**2, axis=-1))
141
+ -
142
+ - sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
143
+ - overlaps = np.maximum(0, sum_radii_pairs - dists)
144
+ - np.fill_diagonal(overlaps, 0)
145
+ -
146
+ - with np.errstate(divide='ignore', invalid='ignore'):
147
+ - unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
148
+ - unit_vectors[dists < 1e-9] = 0
149
+ -
150
+ - circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
151
+ -
152
+ - wall_forces = np.zeros_like(refined_centers)
153
+ - wall_strength = self.config['lsr_wall_strength']
154
+ - wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
155
+ - wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
156
+ - wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
157
+ - wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
158
+ -
159
+ - forces = circle_forces + wall_forces
160
+ - refined_centers += forces * current_lr
161
+ - refined_centers = np.clip(refined_centers, 0.0, 1.0)
162
+ -
163
+ +
164
+ return refined_centers
165
+
166
+ -class MemeticAlgorithm:
167
+ - """Encapsulates the entire Memetic Algorithm for circle packing."""
168
+ - def __init__(self, n, config):
169
+ +
170
+ +class SimulatedAnnealerWithInjection:
171
+ + """
172
+ + Performs a search using SA, with a memetic local search integrated as a move operator.
173
+ + """
174
+ + def __init__(self, n, initial_centers, config):
175
+ self.n = n
176
+ self.config = config
177
+ - self.population = []
178
+ - self.fitnesses = np.array([])
179
+ - self.best_solution = None
180
+ - self.best_fitness = -1.0
181
+ - self.local_search_refiner = LocalSearchRefiner(config=config)
182
+ -
183
+ - def _initialize_population(self):
184
+ - """Initializes a diverse population with both random and grid-based individuals."""
185
+ - num_grid_based = self.config['population_size'] // 3
186
+ -
187
+ - for i in range(self.config['population_size']):
188
+ - if i < num_grid_based:
189
+ - centers = np.zeros((self.n, 2))
190
+ - num_cells_side = 5
191
+ - spacing = 1.0 / num_cells_side
192
+ - k = 0
193
+ - for row in range(num_cells_side):
194
+ - for col in range(num_cells_side):
195
+ - centers[k, 0] = (col + 0.5) * spacing
196
+ - centers[k, 1] = (row + 0.5) * spacing
197
+ - k += 1
198
+ - centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
199
+ -
200
+ - # Place 26th circle in a random-ish interstitial void
201
+ - interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
202
+ - extra_pos = interstitial_points[i % len(interstitial_points)]
203
+ - centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
204
+ - self.population.append(np.clip(centers, 0.0, 1.0))
205
+ - else:
206
+ - self.population.append(np.random.rand(self.n, 2))
207
+ -
208
+ - def _evaluate_population(self):
209
+ - """Calculates fitness for the population and updates the best solution."""
210
+ - fitnesses = []
211
+ - for ind in self.population:
212
+ - radii = compute_max_radii(ind)
213
+ - fitnesses.append(np.sum(radii))
214
+ - self.fitnesses = np.array(fitnesses)
215
+ -
216
+ - best_idx = np.argmax(self.fitnesses)
217
+ - if self.fitnesses[best_idx] > self.best_fitness:
218
+ - self.best_fitness = self.fitnesses[best_idx]
219
+ - self.best_solution = self.population[best_idx].copy()
220
+ -
221
+ - def _select_parent(self):
222
+ - """Selects a parent using tournament selection."""
223
+ - tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
224
+ - tourn_fitnesses = self.fitnesses[tourn_indices]
225
+ - winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
226
+ - return self.population[winner_idx]
227
+ -
228
+ - def _crossover(self, p1, p2):
229
+ - """Performs blend crossover (BLX-alpha)."""
230
+ - alpha = self.config['crossover_alpha']
231
+ - child = alpha * p1 + (1.0 - alpha) * p2
232
+ - return np.clip(child, 0.0, 1.0)
233
+ -
234
+ - def _mutate(self, individual, strength):
235
+ - """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
236
+ - mutated_ind = individual.copy()
237
+ - mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
238
+ - noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
239
+ - mutated_ind[mutation_mask] += noise
240
+ -
241
+ - if np.random.rand() < self.config['jump_mutation_prob']:
242
+ - idx_to_reset = np.random.randint(0, self.n)
243
+ - mutated_ind[idx_to_reset] = np.random.rand(2)
244
+ -
245
+ - return np.clip(mutated_ind, 0.0, 1.0)
246
+ + self.centers = initial_centers
247
+ +
248
+ + self.temp = config['t_start']
249
+ + self.max_step_size = config['max_step_size']
250
+ +
251
+ + self.energy = self._calculate_energy(self.centers)
252
+ + self.best_centers = self.centers.copy()
253
+ + self.best_energy = self.energy
254
+ +
255
+ + self.move_weights = config['move_weights']
256
+ + self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles, self._refinement_injection]
257
+ +
258
+ + self.refiner = QuickRefiner(config)
259
+ +
260
+ + def _calculate_energy(self, centers):
261
+ + """Energy is the negative sum of radii, to be minimized."""
262
+ + radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
263
+ + return -np.sum(radii)
264
+ +
265
+ + def _propose_move(self):
266
+ + move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
267
+ + t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
268
+ + current_step_size = self.max_step_size * t_progress
269
+ + return move_func(current_step_size)
270
+ +
271
+ + def _move_single_circle(self, step_size):
272
+ + new_centers = self.centers.copy()
273
+ + idx = random.randint(0, self.n - 1)
274
+ + displacement = np.random.normal(0, step_size, size=2)
275
+ + new_centers[idx] += displacement
276
+ + new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
277
+ + return new_centers
278
+ +
279
+ + def _move_cluster(self, step_size):
280
+ + new_centers = self.centers.copy()
281
+ + cluster_size = self.config['cluster_size']
282
+ + seed_idx = random.randint(0, self.n - 1)
283
+ + dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
284
+ + neighbor_indices = np.argsort(dists)[:cluster_size]
285
+ + displacement = np.random.normal(0, step_size * 0.5, size=2)
286
+ + new_centers[neighbor_indices] += displacement
287
+ + new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
288
+ + return new_centers
289
+ +
290
+ + def _swap_circles(self, step_size):
291
+ + new_centers = self.centers.copy()
292
+ + if self.n < 2: return new_centers
293
+ + idx1, idx2 = random.sample(range(self.n), 2)
294
+ + new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
295
+ + return new_centers
296
+ +
297
+ + def _refinement_injection(self, step_size):
298
+ + """Applies the quick local search as a single, powerful move."""
299
+ + return self.refiner.refine(self.centers)
300
+
301
+ def run(self):
302
+ - """Executes the full memetic algorithm evolution."""
303
+ - self._initialize_population()
304
+ -
305
+ - for gen in range(self.config['generations']):
306
+ - self._evaluate_population()
307
+ -
308
+ - new_population = []
309
+ -
310
+ - # Elitism: carry over best individuals
311
+ - elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
312
+ - for idx in elite_indices:
313
+ - new_population.append(self.population[idx].copy())
314
+ -
315
+ - # Anneal mutation strength
316
+ - mut_strength = self.config['mut_strength_end'] + \
317
+ - (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
318
+ - (1.0 - (gen / self.config['generations']))**2.0
319
+ -
320
+ - while len(new_population) < self.config['population_size']:
321
+ - p1 = self._select_parent()
322
+ - p2 = self._select_parent()
323
+ -
324
+ - child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
325
+ - child = self._mutate(child, mut_strength)
326
+ -
327
+ - # Memetic step: Apply local search probabilistically
328
+ - if np.random.rand() < self.config['local_search_prob']:
329
+ - child = self.local_search_refiner.refine(child)
330
+ -
331
+ - new_population.append(child)
332
+ -
333
+ - self.population = new_population
334
+ -
335
+ - self._evaluate_population()
336
+ - return self.best_solution
337
+ + """Executes the simulated annealing search."""
338
+ + while self.temp > self.config['t_end']:
339
+ + for _ in range(self.config['moves_per_temp']):
340
+ + new_centers = self._propose_move()
341
+ + new_energy = self._calculate_energy(new_centers)
342
+ +
343
+ + delta_e = new_energy - self.energy
344
+ +
345
+ + if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
346
+ + self.centers = new_centers
347
+ + self.energy = new_energy
348
+ +
349
+ + if self.energy < self.best_energy:
350
+ + self.best_energy = self.energy
351
+ + self.best_centers = self.centers.copy()
352
+ +
353
+ + self.temp *= self.config['cooling_rate']
354
+ +
355
+ + return self.best_centers
356
+ +
357
+
358
+ def construct_packing():
359
+ """
360
+ - Constructs a packing of 26 circles using a Memetic Algorithm.
361
+ + Constructs a packing of 26 circles using a multi-start SA with memetic injection.
362
+ """
363
+ n = 26
364
+ config = {
365
+ - # --- GA Parameters ---
366
+ - 'population_size': 80,
367
+ - 'generations': 350,
368
+ - 'elite_count': 4,
369
+ - 'tournament_size': 6,
370
+ - 'mutation_rate': 0.35, # Per-circle mutation probability
371
+ - 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
372
+ - 'mut_strength_start': 0.1,
373
+ - 'mut_strength_end': 0.001,
374
+ - 'crossover_rate': 0.9,
375
+ - 'crossover_alpha': 0.5, # For BLX-alpha crossover
376
+ - # --- Memetic Parameters ---
377
+ - 'local_search_prob': 0.20, # Probability of applying local search to a new child
378
+ - # --- Local Search Refiner (LSR) Parameters ---
379
+ - 'lsr_aggressive_iter': 40,
380
+ - 'lsr_aggressive_lr': 0.025,
381
+ - 'lsr_aggressive_pressure_start': 1.06,
382
+ - 'lsr_aggressive_pressure_end': 1.001,
383
+ - 'lsr_finetune_iter': 80,
384
+ - 'lsr_finetune_lr': 0.001,
385
+ - 'lsr_finetune_pressure_start': 1.0008,
386
+ - 'lsr_finetune_pressure_end': 1.00001,
387
+ - 'lsr_wall_strength': 0.5,
388
+ + # --- Multi-Start Config ---
389
+ + 'num_starts': 12,
390
+ + # --- SA Config ---
391
+ + 't_start': 0.05,
392
+ + 't_end': 1e-6,
393
+ + 'cooling_rate': 0.9985, # Slower cooling for deeper search
394
+ + 'moves_per_temp': 80,
395
+ + 'max_step_size': 0.15,
396
+ + 'radius_iter': 250,
397
+ + 'move_weights': [0.60, 0.20, 0.15, 0.05], # [single, cluster, swap, refine]
398
+ + 'cluster_size': 4,
399
+ + # --- Refiner Config (for injection) ---
400
+ + 'refiner_iter': 50,
401
+ + 'refiner_lr': 0.02,
402
+ + 'refiner_pressure_start': 1.05,
403
+ + 'refiner_pressure_end': 1.001,
404
+ + 'refiner_wall_strength': 0.5,
405
+ }
406
+
407
+ - solver = MemeticAlgorithm(n=n, config=config)
408
+ - best_centers = solver.run()
409
+ -
410
+ - # Final, high-precision radius calculation for the best solution
411
+ - final_radii = compute_max_radii(best_centers)
412
+ -
413
+ - return best_centers, final_radii
414
+ -
415
+ -
416
+ -def compute_max_radii(centers):
417
+ - """
418
+ - Compute the maximum possible radii for each circle position
419
+ - such that they don't overlap and stay within the unit square.
420
+ - This uses an iterative proportional scaling method.
421
+ - """
422
+ - n = centers.shape[0]
423
+ - radii = np.ones(n)
424
+ -
425
+ - for i in range(n):
426
+ - x, y = centers[i]
427
+ - radii[i] = min(x, y, 1 - x, 1 - y)
428
+ -
429
+ - max_radius_iter = 600 # Maximum iterations for radius calculation
430
+ - convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
431
+ - required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
432
+ - converged_in_a_row = 0 # Counter for consecutive converged iterations
433
+ -
434
+ - for iteration in range(max_radius_iter):
435
+ - previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
436
+ -
437
+ - for i in range(n):
438
+ - for j in range(i + 1, n):
439
+ - dist = np.linalg.norm(centers[i] - centers[j])
440
+ -
441
+ - if dist < 1e-9: # Centers are practically identical
442
+ - radii[i] = 0.0
443
+ - radii[j] = 0.0
444
+ - # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
445
+ - continue
446
+ -
447
+ - if radii[i] + radii[j] > dist:
448
+ - scale = dist / (radii[i] + radii[j])
449
+ - radii[i] *= scale
450
+ - radii[j] *= scale
451
+ -
452
+ - # Calculate the maximum absolute change in any radius
453
+ - max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
454
+ -
455
+ - # Check for convergence based on max_absolute_change
456
+ - if max_absolute_change < convergence_epsilon:
457
+ - converged_in_a_row += 1
458
+ - if converged_in_a_row >= required_converged_iters:
459
+ - break # Converged for several consecutive iterations
460
+ + best_overall_centers = None
461
+ + best_overall_score = -1.0
462
+ +
463
+ + for i in range(config['num_starts']):
464
+ + initial_centers = np.zeros((n, 2))
465
+ + # --- Strategic Initialization (from best prior SA) ---
466
+ + if i < config['num_starts'] // 2:
467
+ + num_cells_side = 5
468
+ + spacing = 1.0 / num_cells_side
469
+ + k = 0
470
+ + for row in range(num_cells_side):
471
+ + for col in range(num_cells_side):
472
+ + initial_centers[k, 0] = (col + 0.5) * spacing
473
+ + initial_centers[k, 1] = (row + 0.5) * spacing
474
+ + k += 1
475
+ + initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
476
+ +
477
+ + extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
478
+ + initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
479
+ + initial_centers = np.clip(initial_centers, 0.01, 0.99)
480
+ else:
481
+ - converged_in_a_row = 0 # Reset counter if change is significant
482
+ -
483
+ - return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
484
+ + initial_centers = np.random.rand(n, 2)
485
+ +
486
+ + solver = SimulatedAnnealerWithInjection(n=n, initial_centers=initial_centers, config=config)
487
+ + result_centers = solver.run()
488
+ +
489
+ + radii = compute_max_radii(result_centers, max_iter=2500, convergence_threshold=1e-9)
490
+ + score = np.sum(radii)
491
+ +
492
+ + if score > best_overall_score:
493
+ + best_overall_score = score
494
+ + best_overall_centers = result_centers
495
+ +
496
+ + final_radii = compute_max_radii(best_overall_centers, max_iter=4000, convergence_threshold=1e-9)
497
+ +
498
+ + return best_overall_centers, final_radii
499
+ # EVOLVE-BLOCK-END
500
+
501
+
502
+ # This part remains fixed (not evolved)
503
+ def run_packing():
504
+ """Run the circle packing constructor for n=26"""
505
+ centers, radii = construct_packing()
506
+ # Calculate the sum of radii
507
+ sum_radii = np.sum(radii)
508
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/main.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ import math
4
+ import random
5
+
6
+ def compute_max_radii(centers, max_iter=800, convergence_threshold=1e-8):
7
+ """
8
+ Compute maximum radii using iterative proportional scaling.
9
+ This is a robust version with a stable convergence check.
10
+ """
11
+ n = centers.shape[0]
12
+ if n == 0:
13
+ return np.array([])
14
+
15
+ radii = np.min([
16
+ centers[:, 0], 1 - centers[:, 0],
17
+ centers[:, 1], 1 - centers[:, 1]
18
+ ], axis=0)
19
+
20
+ required_converged_iters = 3
21
+ converged_in_a_row = 0
22
+
23
+ for _ in range(max_iter):
24
+ old_radii = radii.copy()
25
+
26
+ for i in range(n):
27
+ for j in range(i + 1, n):
28
+ dist_sq = np.sum((centers[i] - centers[j])**2)
29
+ dist = np.sqrt(dist_sq)
30
+
31
+ if radii[i] + radii[j] > dist:
32
+ if dist < 1e-9:
33
+ radii[i], radii[j] = 0.0, 0.0
34
+ else:
35
+ scale = dist / (radii[i] + radii[j])
36
+ radii[i] *= scale
37
+ radii[j] *= scale
38
+
39
+ max_abs_change = np.max(np.abs(radii - old_radii))
40
+ if max_abs_change < convergence_threshold:
41
+ converged_in_a_row += 1
42
+ if converged_in_a_row >= required_converged_iters:
43
+ break
44
+ else:
45
+ converged_in_a_row = 0
46
+
47
+ return np.maximum(radii, 0)
48
+
49
+
50
+ class QuickRefiner:
51
+ """
52
+ A fast, aggressive, single-phase local search operator for memetic injection.
53
+ """
54
+ def __init__(self, config):
55
+ self.config = config
56
+
57
+ def refine(self, centers):
58
+ refined_centers = centers.copy()
59
+
60
+ for sim_iter in range(self.config['refiner_iter']):
61
+ progress = sim_iter / self.config['refiner_iter']
62
+ current_lr = self.config['refiner_lr'] * (1.0 - progress)**2
63
+ current_pressure = self.config['refiner_pressure_end'] + \
64
+ (self.config['refiner_pressure_start'] - self.config['refiner_pressure_end']) * (1.0 - progress)**2
65
+
66
+ # Fast radius calculation for simulation
67
+ radii = compute_max_radii(refined_centers, max_iter=100)
68
+ inflated_radii = radii * current_pressure
69
+
70
+ # Vectorized force calculations
71
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
72
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
73
+ dists[dists < 1e-9] = 1e-9
74
+
75
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
76
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
77
+ np.fill_diagonal(overlaps, 0)
78
+
79
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
80
+ circle_forces = np.sum(force_matrix, axis=1)
81
+
82
+ wall_forces = np.zeros_like(refined_centers)
83
+ wall_strength = self.config['refiner_wall_strength']
84
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
85
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
86
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
87
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
88
+
89
+ forces = circle_forces + wall_forces
90
+ refined_centers += forces * current_lr
91
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
92
+
93
+ return refined_centers
94
+
95
+
96
+ class SimulatedAnnealerWithInjection:
97
+ """
98
+ Performs a search using SA, with a memetic local search integrated as a move operator.
99
+ """
100
+ def __init__(self, n, initial_centers, config):
101
+ self.n = n
102
+ self.config = config
103
+ self.centers = initial_centers
104
+
105
+ self.temp = config['t_start']
106
+ self.max_step_size = config['max_step_size']
107
+
108
+ self.energy = self._calculate_energy(self.centers)
109
+ self.best_centers = self.centers.copy()
110
+ self.best_energy = self.energy
111
+
112
+ self.move_weights = config['move_weights']
113
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles, self._refinement_injection]
114
+
115
+ self.refiner = QuickRefiner(config)
116
+
117
+ def _calculate_energy(self, centers):
118
+ """Energy is the negative sum of radii, to be minimized."""
119
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
120
+ return -np.sum(radii)
121
+
122
+ def _propose_move(self):
123
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
124
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
125
+ current_step_size = self.max_step_size * t_progress
126
+ return move_func(current_step_size)
127
+
128
+ def _move_single_circle(self, step_size):
129
+ new_centers = self.centers.copy()
130
+ idx = random.randint(0, self.n - 1)
131
+ displacement = np.random.normal(0, step_size, size=2)
132
+ new_centers[idx] += displacement
133
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
134
+ return new_centers
135
+
136
+ def _move_cluster(self, step_size):
137
+ new_centers = self.centers.copy()
138
+ cluster_size = self.config['cluster_size']
139
+ seed_idx = random.randint(0, self.n - 1)
140
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
141
+ neighbor_indices = np.argsort(dists)[:cluster_size]
142
+ displacement = np.random.normal(0, step_size * 0.5, size=2)
143
+ new_centers[neighbor_indices] += displacement
144
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
145
+ return new_centers
146
+
147
+ def _swap_circles(self, step_size):
148
+ new_centers = self.centers.copy()
149
+ if self.n < 2: return new_centers
150
+ idx1, idx2 = random.sample(range(self.n), 2)
151
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
152
+ return new_centers
153
+
154
+ def _refinement_injection(self, step_size):
155
+ """Applies the quick local search as a single, powerful move."""
156
+ return self.refiner.refine(self.centers)
157
+
158
+ def run(self):
159
+ """Executes the simulated annealing search."""
160
+ while self.temp > self.config['t_end']:
161
+ for _ in range(self.config['moves_per_temp']):
162
+ new_centers = self._propose_move()
163
+ new_energy = self._calculate_energy(new_centers)
164
+
165
+ delta_e = new_energy - self.energy
166
+
167
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
168
+ self.centers = new_centers
169
+ self.energy = new_energy
170
+
171
+ if self.energy < self.best_energy:
172
+ self.best_energy = self.energy
173
+ self.best_centers = self.centers.copy()
174
+
175
+ self.temp *= self.config['cooling_rate']
176
+
177
+ return self.best_centers
178
+
179
+
180
+ def construct_packing():
181
+ """
182
+ Constructs a packing of 26 circles using a multi-start SA with memetic injection.
183
+ """
184
+ n = 26
185
+ config = {
186
+ # --- Multi-Start Config ---
187
+ 'num_starts': 12,
188
+ # --- SA Config ---
189
+ 't_start': 0.05,
190
+ 't_end': 1e-6,
191
+ 'cooling_rate': 0.9985, # Slower cooling for deeper search
192
+ 'moves_per_temp': 80,
193
+ 'max_step_size': 0.15,
194
+ 'radius_iter': 250,
195
+ 'move_weights': [0.60, 0.20, 0.15, 0.05], # [single, cluster, swap, refine]
196
+ 'cluster_size': 4,
197
+ # --- Refiner Config (for injection) ---
198
+ 'refiner_iter': 50,
199
+ 'refiner_lr': 0.02,
200
+ 'refiner_pressure_start': 1.05,
201
+ 'refiner_pressure_end': 1.001,
202
+ 'refiner_wall_strength': 0.5,
203
+ }
204
+
205
+ best_overall_centers = None
206
+ best_overall_score = -1.0
207
+
208
+ for i in range(config['num_starts']):
209
+ initial_centers = np.zeros((n, 2))
210
+ # --- Strategic Initialization (from best prior SA) ---
211
+ if i < config['num_starts'] // 2:
212
+ num_cells_side = 5
213
+ spacing = 1.0 / num_cells_side
214
+ k = 0
215
+ for row in range(num_cells_side):
216
+ for col in range(num_cells_side):
217
+ initial_centers[k, 0] = (col + 0.5) * spacing
218
+ initial_centers[k, 1] = (row + 0.5) * spacing
219
+ k += 1
220
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
221
+
222
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
223
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
224
+ initial_centers = np.clip(initial_centers, 0.01, 0.99)
225
+ else:
226
+ initial_centers = np.random.rand(n, 2)
227
+
228
+ solver = SimulatedAnnealerWithInjection(n=n, initial_centers=initial_centers, config=config)
229
+ result_centers = solver.run()
230
+
231
+ radii = compute_max_radii(result_centers, max_iter=2500, convergence_threshold=1e-9)
232
+ score = np.sum(radii)
233
+
234
+ if score > best_overall_score:
235
+ best_overall_score = score
236
+ best_overall_centers = result_centers
237
+
238
+ final_radii = compute_max_radii(best_overall_centers, max_iter=4000, convergence_threshold=1e-9)
239
+
240
+ return best_overall_centers, final_radii
241
+ # EVOLVE-BLOCK-END
242
+
243
+
244
+ # This part remains fixed (not evolved)
245
+ def run_packing():
246
+ """Run the circle packing constructor for n=26"""
247
+ centers, radii = construct_packing()
248
+ # Calculate the sum of radii
249
+ sum_radii = np.sum(radii)
250
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/original.py ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ This solution implements a Memetic Algorithm (MA), which combines a Genetic
4
+ Algorithm (GA) for global exploration with a powerful, physics-based local search
5
+ for exploitation. This approach hybridizes the successful population-based
6
+ framework of prior submissions with the refined, two-stage force-directed
7
+ simulation from the current program, which will be repurposed as the local
8
+ search operator.
9
+ """
10
+
11
+ import numpy as np
12
+
13
+ class LocalSearchRefiner:
14
+ """
15
+ Performs a fast, two-stage, force-directed local refinement on a set of centers.
16
+ This is adapted from the full simulation of the parent program to act as a
17
+ powerful local search operator within the Memetic Algorithm.
18
+ """
19
+ def __init__(self, config):
20
+ self.config = config
21
+
22
+ def refine(self, centers):
23
+ """Applies a two-stage force-directed refinement to polish a solution."""
24
+ refined_centers = centers.copy()
25
+ n = refined_centers.shape[0]
26
+
27
+ # --- Stage 1: Aggressive "unfreezing" and resettlement ---
28
+ for sim_iter in range(self.config['lsr_aggressive_iter']):
29
+ progress = sim_iter / self.config['lsr_aggressive_iter']
30
+ current_lr = self.config['lsr_aggressive_lr'] * (1.0 - progress)**2
31
+ current_pressure = self.config['lsr_aggressive_pressure_end'] + \
32
+ (self.config['lsr_aggressive_pressure_start'] - self.config['lsr_aggressive_pressure_end']) * (1.0 - progress)**2
33
+
34
+ radii = compute_max_radii(refined_centers)
35
+ inflated_radii = radii * current_pressure
36
+
37
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
38
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
39
+
40
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
41
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
42
+ np.fill_diagonal(overlaps, 0)
43
+
44
+ with np.errstate(divide='ignore', invalid='ignore'):
45
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
46
+ unit_vectors[dists < 1e-9] = 0
47
+
48
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
49
+
50
+ wall_forces = np.zeros_like(refined_centers)
51
+ wall_strength = self.config['lsr_wall_strength']
52
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
53
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
54
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
55
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
56
+
57
+ forces = circle_forces + wall_forces
58
+ refined_centers += forces * current_lr
59
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
60
+
61
+ # --- Stage 2: Fine-tuning with subtle growth pressure ---
62
+ for sim_iter_ft in range(self.config['lsr_finetune_iter']):
63
+ progress = sim_iter_ft / self.config['lsr_finetune_iter']
64
+ current_lr = self.config['lsr_finetune_lr'] * (1.0 - progress)
65
+ current_pressure = self.config['lsr_finetune_pressure_end'] + \
66
+ (self.config['lsr_finetune_pressure_start'] - self.config['lsr_finetune_pressure_end']) * (1.0 - progress)
67
+
68
+ radii = compute_max_radii(refined_centers)
69
+ inflated_radii = radii * current_pressure
70
+
71
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
72
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
73
+
74
+ sum_radii_pairs = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
75
+ overlaps = np.maximum(0, sum_radii_pairs - dists)
76
+ np.fill_diagonal(overlaps, 0)
77
+
78
+ with np.errstate(divide='ignore', invalid='ignore'):
79
+ unit_vectors = diffs / (dists[..., np.newaxis] + 1e-9)
80
+ unit_vectors[dists < 1e-9] = 0
81
+
82
+ circle_forces = np.sum(unit_vectors * overlaps[..., np.newaxis], axis=1)
83
+
84
+ wall_forces = np.zeros_like(refined_centers)
85
+ wall_strength = self.config['lsr_wall_strength']
86
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
87
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
88
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
89
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
90
+
91
+ forces = circle_forces + wall_forces
92
+ refined_centers += forces * current_lr
93
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
94
+
95
+ return refined_centers
96
+
97
+ class MemeticAlgorithm:
98
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
99
+ def __init__(self, n, config):
100
+ self.n = n
101
+ self.config = config
102
+ self.population = []
103
+ self.fitnesses = np.array([])
104
+ self.best_solution = None
105
+ self.best_fitness = -1.0
106
+ self.local_search_refiner = LocalSearchRefiner(config=config)
107
+
108
+ def _initialize_population(self):
109
+ """Initializes a diverse population with both random and grid-based individuals."""
110
+ num_grid_based = self.config['population_size'] // 3
111
+
112
+ for i in range(self.config['population_size']):
113
+ if i < num_grid_based:
114
+ centers = np.zeros((self.n, 2))
115
+ num_cells_side = 5
116
+ spacing = 1.0 / num_cells_side
117
+ k = 0
118
+ for row in range(num_cells_side):
119
+ for col in range(num_cells_side):
120
+ centers[k, 0] = (col + 0.5) * spacing
121
+ centers[k, 1] = (row + 0.5) * spacing
122
+ k += 1
123
+ centers[:25, :] += np.random.normal(0, spacing * 0.02, size=(25, 2))
124
+
125
+ # Place 26th circle in a random-ish interstitial void
126
+ interstitial_points = [[0.2, 0.2], [0.2, 0.8], [0.8, 0.2], [0.8, 0.8], [0.5, 0.5]]
127
+ extra_pos = interstitial_points[i % len(interstitial_points)]
128
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.05, size=2)
129
+ self.population.append(np.clip(centers, 0.0, 1.0))
130
+ else:
131
+ self.population.append(np.random.rand(self.n, 2))
132
+
133
+ def _evaluate_population(self):
134
+ """Calculates fitness for the population and updates the best solution."""
135
+ fitnesses = []
136
+ for ind in self.population:
137
+ radii = compute_max_radii(ind)
138
+ fitnesses.append(np.sum(radii))
139
+ self.fitnesses = np.array(fitnesses)
140
+
141
+ best_idx = np.argmax(self.fitnesses)
142
+ if self.fitnesses[best_idx] > self.best_fitness:
143
+ self.best_fitness = self.fitnesses[best_idx]
144
+ self.best_solution = self.population[best_idx].copy()
145
+
146
+ def _select_parent(self):
147
+ """Selects a parent using tournament selection."""
148
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
149
+ tourn_fitnesses = self.fitnesses[tourn_indices]
150
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
151
+ return self.population[winner_idx]
152
+
153
+ def _crossover(self, p1, p2):
154
+ """Performs blend crossover (BLX-alpha)."""
155
+ alpha = self.config['crossover_alpha']
156
+ child = alpha * p1 + (1.0 - alpha) * p2
157
+ return np.clip(child, 0.0, 1.0)
158
+
159
+ def _mutate(self, individual, strength):
160
+ """Applies Gaussian mutation and a chance of a strong mutation (jump)."""
161
+ mutated_ind = individual.copy()
162
+ mutation_mask = np.random.rand(self.n) < self.config['mutation_rate']
163
+ noise = np.random.normal(0, strength, size=(np.sum(mutation_mask), 2))
164
+ mutated_ind[mutation_mask] += noise
165
+
166
+ if np.random.rand() < self.config['jump_mutation_prob']:
167
+ idx_to_reset = np.random.randint(0, self.n)
168
+ mutated_ind[idx_to_reset] = np.random.rand(2)
169
+
170
+ return np.clip(mutated_ind, 0.0, 1.0)
171
+
172
+ def run(self):
173
+ """Executes the full memetic algorithm evolution."""
174
+ self._initialize_population()
175
+
176
+ for gen in range(self.config['generations']):
177
+ self._evaluate_population()
178
+
179
+ new_population = []
180
+
181
+ # Elitism: carry over best individuals
182
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
183
+ for idx in elite_indices:
184
+ new_population.append(self.population[idx].copy())
185
+
186
+ # Anneal mutation strength
187
+ mut_strength = self.config['mut_strength_end'] + \
188
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
189
+ (1.0 - (gen / self.config['generations']))**2.0
190
+
191
+ while len(new_population) < self.config['population_size']:
192
+ p1 = self._select_parent()
193
+ p2 = self._select_parent()
194
+
195
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
196
+ child = self._mutate(child, mut_strength)
197
+
198
+ # Memetic step: Apply local search probabilistically
199
+ if np.random.rand() < self.config['local_search_prob']:
200
+ child = self.local_search_refiner.refine(child)
201
+
202
+ new_population.append(child)
203
+
204
+ self.population = new_population
205
+
206
+ self._evaluate_population()
207
+ return self.best_solution
208
+
209
+ def construct_packing():
210
+ """
211
+ Constructs a packing of 26 circles using a Memetic Algorithm.
212
+ """
213
+ n = 26
214
+ config = {
215
+ # --- GA Parameters ---
216
+ 'population_size': 80,
217
+ 'generations': 350,
218
+ 'elite_count': 4,
219
+ 'tournament_size': 6,
220
+ 'mutation_rate': 0.35, # Per-circle mutation probability
221
+ 'jump_mutation_prob': 0.03, # Probability of a random reset for one circle
222
+ 'mut_strength_start': 0.1,
223
+ 'mut_strength_end': 0.001,
224
+ 'crossover_rate': 0.9,
225
+ 'crossover_alpha': 0.5, # For BLX-alpha crossover
226
+ # --- Memetic Parameters ---
227
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
228
+ # --- Local Search Refiner (LSR) Parameters ---
229
+ 'lsr_aggressive_iter': 40,
230
+ 'lsr_aggressive_lr': 0.025,
231
+ 'lsr_aggressive_pressure_start': 1.06,
232
+ 'lsr_aggressive_pressure_end': 1.001,
233
+ 'lsr_finetune_iter': 80,
234
+ 'lsr_finetune_lr': 0.001,
235
+ 'lsr_finetune_pressure_start': 1.0008,
236
+ 'lsr_finetune_pressure_end': 1.00001,
237
+ 'lsr_wall_strength': 0.5,
238
+ }
239
+
240
+ solver = MemeticAlgorithm(n=n, config=config)
241
+ best_centers = solver.run()
242
+
243
+ # Final, high-precision radius calculation for the best solution
244
+ final_radii = compute_max_radii(best_centers)
245
+
246
+ return best_centers, final_radii
247
+
248
+
249
+ def compute_max_radii(centers):
250
+ """
251
+ Compute the maximum possible radii for each circle position
252
+ such that they don't overlap and stay within the unit square.
253
+ This uses an iterative proportional scaling method.
254
+ """
255
+ n = centers.shape[0]
256
+ radii = np.ones(n)
257
+
258
+ for i in range(n):
259
+ x, y = centers[i]
260
+ radii[i] = min(x, y, 1 - x, 1 - y)
261
+
262
+ max_radius_iter = 600 # Maximum iterations for radius calculation
263
+ convergence_epsilon = 1e-7 # Threshold for maximum absolute change in radii
264
+ required_converged_iters = 5 # Number of consecutive iterations below epsilon to confirm convergence
265
+ converged_in_a_row = 0 # Counter for consecutive converged iterations
266
+
267
+ for iteration in range(max_radius_iter):
268
+ previous_radii = radii.copy() # Store radii from the previous iteration to check for changes
269
+
270
+ for i in range(n):
271
+ for j in range(i + 1, n):
272
+ dist = np.linalg.norm(centers[i] - centers[j])
273
+
274
+ if dist < 1e-9: # Centers are practically identical
275
+ radii[i] = 0.0
276
+ radii[j] = 0.0
277
+ # Note: No explicit `updated_in_pass` needed anymore; `max_absolute_change` captures this.
278
+ continue
279
+
280
+ if radii[i] + radii[j] > dist:
281
+ scale = dist / (radii[i] + radii[j])
282
+ radii[i] *= scale
283
+ radii[j] *= scale
284
+
285
+ # Calculate the maximum absolute change in any radius
286
+ max_absolute_change = np.max(np.abs(radii - previous_radii)) if n > 0 else 0.0
287
+
288
+ # Check for convergence based on max_absolute_change
289
+ if max_absolute_change < convergence_epsilon:
290
+ converged_in_a_row += 1
291
+ if converged_in_a_row >= required_converged_iters:
292
+ break # Converged for several consecutive iterations
293
+ else:
294
+ converged_in_a_row = 0 # Reset counter if change is significant
295
+
296
+ return np.maximum(radii, 0.0) # Ensure no negative radii (should be handled by logic, but as a safeguard)
297
+ # EVOLVE-BLOCK-END
298
+
299
+
300
+ # This part remains fixed (not evolved)
301
+ def run_packing():
302
+ """Run the circle packing constructor for n=26"""
303
+ centers, radii = construct_packing()
304
+ # Calculate the sum of radii
305
+ sum_radii = np.sum(radii)
306
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results/job_log.err ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
2
+
3
+ All support for the `google.generativeai` package has ended. It will no longer be receiving
4
+ updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
5
+ See README for more details:
6
+
7
+ https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
8
+
9
+ import google.generativeai as genai # type: ignore[import-not-found]
10
+ Traceback (most recent call last):
11
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/evaluate.py", line 302, in <module>
12
+ main(parsed_args.program_path, parsed_args.results_dir)
13
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/evaluate.py", line 262, in main
14
+ metrics, correct, error_msg = run_shinka_eval(
15
+ ^^^^^^^^^^^^^^^^
16
+ File "/home/tengxiao/pj/ShinkaEvolve/shinka/core/wrap_eval.py", line 119, in run_shinka_eval
17
+ run_result = experiment_fn(**kwargs)
18
+ ^^^^^^^^^^^^^^^^^^^^^^^
19
+ File "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/main.py", line 247, in run_packing
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results/job_log.out ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/main.py
2
+ Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/results
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_100/rewrite.txt ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ import math
4
+ import random
5
+
6
+ def compute_max_radii(centers, max_iter=800, convergence_threshold=1e-8):
7
+ """
8
+ Compute maximum radii using iterative proportional scaling.
9
+ This is a robust version with a stable convergence check.
10
+ """
11
+ n = centers.shape[0]
12
+ if n == 0:
13
+ return np.array([])
14
+
15
+ radii = np.min([
16
+ centers[:, 0], 1 - centers[:, 0],
17
+ centers[:, 1], 1 - centers[:, 1]
18
+ ], axis=0)
19
+
20
+ required_converged_iters = 3
21
+ converged_in_a_row = 0
22
+
23
+ for _ in range(max_iter):
24
+ old_radii = radii.copy()
25
+
26
+ for i in range(n):
27
+ for j in range(i + 1, n):
28
+ dist_sq = np.sum((centers[i] - centers[j])**2)
29
+ dist = np.sqrt(dist_sq)
30
+
31
+ if radii[i] + radii[j] > dist:
32
+ if dist < 1e-9:
33
+ radii[i], radii[j] = 0.0, 0.0
34
+ else:
35
+ scale = dist / (radii[i] + radii[j])
36
+ radii[i] *= scale
37
+ radii[j] *= scale
38
+
39
+ max_abs_change = np.max(np.abs(radii - old_radii))
40
+ if max_abs_change < convergence_threshold:
41
+ converged_in_a_row += 1
42
+ if converged_in_a_row >= required_converged_iters:
43
+ break
44
+ else:
45
+ converged_in_a_row = 0
46
+
47
+ return np.maximum(radii, 0)
48
+
49
+
50
+ class QuickRefiner:
51
+ """
52
+ A fast, aggressive, single-phase local search operator for memetic injection.
53
+ """
54
+ def __init__(self, config):
55
+ self.config = config
56
+
57
+ def refine(self, centers):
58
+ refined_centers = centers.copy()
59
+
60
+ for sim_iter in range(self.config['refiner_iter']):
61
+ progress = sim_iter / self.config['refiner_iter']
62
+ current_lr = self.config['refiner_lr'] * (1.0 - progress)**2
63
+ current_pressure = self.config['refiner_pressure_end'] + \
64
+ (self.config['refiner_pressure_start'] - self.config['refiner_pressure_end']) * (1.0 - progress)**2
65
+
66
+ # Fast radius calculation for simulation
67
+ radii = compute_max_radii(refined_centers, max_iter=100)
68
+ inflated_radii = radii * current_pressure
69
+
70
+ # Vectorized force calculations
71
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
72
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
73
+ dists[dists < 1e-9] = 1e-9
74
+
75
+ sum_inflated_radii = inflated_radii[:, np.newaxis] + inflated_radii[np.newaxis, :]
76
+ overlaps = np.maximum(0, sum_inflated_radii - dists)
77
+ np.fill_diagonal(overlaps, 0)
78
+
79
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
80
+ circle_forces = np.sum(force_matrix, axis=1)
81
+
82
+ wall_forces = np.zeros_like(refined_centers)
83
+ wall_strength = self.config['refiner_wall_strength']
84
+ wall_forces[:, 0] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 0])
85
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + inflated_radii) - 1.0)
86
+ wall_forces[:, 1] += wall_strength * np.maximum(0, inflated_radii - refined_centers[:, 1])
87
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + inflated_radii) - 1.0)
88
+
89
+ forces = circle_forces + wall_forces
90
+ refined_centers += forces * current_lr
91
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
92
+
93
+ return refined_centers
94
+
95
+
96
+ class SimulatedAnnealerWithInjection:
97
+ """
98
+ Performs a search using SA, with a memetic local search integrated as a move operator.
99
+ """
100
+ def __init__(self, n, initial_centers, config):
101
+ self.n = n
102
+ self.config = config
103
+ self.centers = initial_centers
104
+
105
+ self.temp = config['t_start']
106
+ self.max_step_size = config['max_step_size']
107
+
108
+ self.energy = self._calculate_energy(self.centers)
109
+ self.best_centers = self.centers.copy()
110
+ self.best_energy = self.energy
111
+
112
+ self.move_weights = config['move_weights']
113
+ self.move_types = [self._move_single_circle, self._move_cluster, self._swap_circles, self._refinement_injection]
114
+
115
+ self.refiner = QuickRefiner(config)
116
+
117
+ def _calculate_energy(self, centers):
118
+ """Energy is the negative sum of radii, to be minimized."""
119
+ radii = compute_max_radii(centers, max_iter=self.config['radius_iter'])
120
+ return -np.sum(radii)
121
+
122
+ def _propose_move(self):
123
+ move_func = random.choices(self.move_types, weights=self.move_weights, k=1)[0]
124
+ t_progress = (self.temp - self.config['t_end']) / (self.config['t_start'] - self.config['t_end'])
125
+ current_step_size = self.max_step_size * t_progress
126
+ return move_func(current_step_size)
127
+
128
+ def _move_single_circle(self, step_size):
129
+ new_centers = self.centers.copy()
130
+ idx = random.randint(0, self.n - 1)
131
+ displacement = np.random.normal(0, step_size, size=2)
132
+ new_centers[idx] += displacement
133
+ new_centers[idx] = np.clip(new_centers[idx], 0.0, 1.0)
134
+ return new_centers
135
+
136
+ def _move_cluster(self, step_size):
137
+ new_centers = self.centers.copy()
138
+ cluster_size = self.config['cluster_size']
139
+ seed_idx = random.randint(0, self.n - 1)
140
+ dists = np.linalg.norm(self.centers - self.centers[seed_idx], axis=1)
141
+ neighbor_indices = np.argsort(dists)[:cluster_size]
142
+ displacement = np.random.normal(0, step_size * 0.5, size=2)
143
+ new_centers[neighbor_indices] += displacement
144
+ new_centers[neighbor_indices] = np.clip(new_centers[neighbor_indices], 0.0, 1.0)
145
+ return new_centers
146
+
147
+ def _swap_circles(self, step_size):
148
+ new_centers = self.centers.copy()
149
+ if self.n < 2: return new_centers
150
+ idx1, idx2 = random.sample(range(self.n), 2)
151
+ new_centers[idx1], new_centers[idx2] = new_centers[idx2].copy(), new_centers[idx1].copy()
152
+ return new_centers
153
+
154
+ def _refinement_injection(self, step_size):
155
+ """Applies the quick local search as a single, powerful move."""
156
+ return self.refiner.refine(self.centers)
157
+
158
+ def run(self):
159
+ """Executes the simulated annealing search."""
160
+ while self.temp > self.config['t_end']:
161
+ for _ in range(self.config['moves_per_temp']):
162
+ new_centers = self._propose_move()
163
+ new_energy = self._calculate_energy(new_centers)
164
+
165
+ delta_e = new_energy - self.energy
166
+
167
+ if delta_e < 0 or random.random() < math.exp(-delta_e / self.temp):
168
+ self.centers = new_centers
169
+ self.energy = new_energy
170
+
171
+ if self.energy < self.best_energy:
172
+ self.best_energy = self.energy
173
+ self.best_centers = self.centers.copy()
174
+
175
+ self.temp *= self.config['cooling_rate']
176
+
177
+ return self.best_centers
178
+
179
+
180
+ def construct_packing():
181
+ """
182
+ Constructs a packing of 26 circles using a multi-start SA with memetic injection.
183
+ """
184
+ n = 26
185
+ config = {
186
+ # --- Multi-Start Config ---
187
+ 'num_starts': 12,
188
+ # --- SA Config ---
189
+ 't_start': 0.05,
190
+ 't_end': 1e-6,
191
+ 'cooling_rate': 0.9985, # Slower cooling for deeper search
192
+ 'moves_per_temp': 80,
193
+ 'max_step_size': 0.15,
194
+ 'radius_iter': 250,
195
+ 'move_weights': [0.60, 0.20, 0.15, 0.05], # [single, cluster, swap, refine]
196
+ 'cluster_size': 4,
197
+ # --- Refiner Config (for injection) ---
198
+ 'refiner_iter': 50,
199
+ 'refiner_lr': 0.02,
200
+ 'refiner_pressure_start': 1.05,
201
+ 'refiner_pressure_end': 1.001,
202
+ 'refiner_wall_strength': 0.5,
203
+ }
204
+
205
+ best_overall_centers = None
206
+ best_overall_score = -1.0
207
+
208
+ for i in range(config['num_starts']):
209
+ initial_centers = np.zeros((n, 2))
210
+ # --- Strategic Initialization (from best prior SA) ---
211
+ if i < config['num_starts'] // 2:
212
+ num_cells_side = 5
213
+ spacing = 1.0 / num_cells_side
214
+ k = 0
215
+ for row in range(num_cells_side):
216
+ for col in range(num_cells_side):
217
+ initial_centers[k, 0] = (col + 0.5) * spacing
218
+ initial_centers[k, 1] = (row + 0.5) * spacing
219
+ k += 1
220
+ initial_centers[:25, :] += np.random.normal(0, spacing * 0.1, size=(25, 2))
221
+
222
+ extra_pos_candidates = [[0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9], [0.5, 0.5]]
223
+ initial_centers[25] = extra_pos_candidates[i % len(extra_pos_candidates)]
224
+ initial_centers = np.clip(initial_centers, 0.01, 0.99)
225
+ else:
226
+ initial_centers = np.random.rand(n, 2)
227
+
228
+ solver = SimulatedAnnealerWithInjection(n=n, initial_centers=initial_centers, config=config)
229
+ result_centers = solver.run()
230
+
231
+ radii = compute_max_radii(result_centers, max_iter=2500, convergence_threshold=1e-9)
232
+ score = np.sum(radii)
233
+
234
+ if score > best_overall_score:
235
+ best_overall_score = score
236
+ best_overall_centers = result_centers
237
+
238
+ final_radii = compute_max_radii(best_overall_centers, max_iter=4000, convergence_threshold=1e-9)
239
+
240
+ return best_overall_centers, final_radii
241
+ # EVOLVE-BLOCK-END
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/__pycache__/main.cpython-311.pyc ADDED
Binary file (18.1 kB). View file
 
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/edit.diff ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,283 +1,315 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+
7
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
8
+ """
9
+ Compute maximum radii using iterative proportional scaling with a convergence check.
10
+ """
11
+ n = centers.shape[0]
12
+ + # Initialize radii to be constrained by walls
13
+ radii = np.min([
14
+ centers[:, 0], 1 - centers[:, 0],
15
+ centers[:, 1], 1 - centers[:, 1]
16
+ ], axis=0)
17
+ -
18
+ +
19
+ for _ in range(max_iter):
20
+ old_radii = radii.copy()
21
+ updated = False
22
+ + # Calculate distances and handle overlaps for all pairs
23
+ + # Vectorized calculation for distances to improve performance
24
+ + diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
25
+ + dists = np.sqrt(np.sum(diffs**2, axis=-1))
26
+ +
27
+ + # Iterate over upper triangle to avoid duplicate calculations and self-comparison
28
+ for i in range(n):
29
+ for j in range(i + 1, n):
30
+ - dist = np.linalg.norm(centers[i] - centers[j])
31
+ + dist = dists[i, j] # Pre-calculated distance
32
+ +
33
+ if radii[i] + radii[j] > dist:
34
+ - if dist < 1e-9:
35
+ - radii[i], radii[j] = 0.0, 0.0
36
+ + if dist < 1e-9: # Handle co-located centers: radii must be zero
37
+ + if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
38
+ + radii[i], radii[j] = 0.0, 0.0
39
+ + updated = True
40
+ else:
41
+ scale = dist / (radii[i] + radii[j])
42
+ radii[i] *= scale
43
+ radii[j] *= scale
44
+ - updated = True
45
+ -
46
+ + updated = True
47
+ +
48
+ + # Check for convergence: if no updates were made AND the max change is tiny.
49
+ + # This prevents infinite loops if changes are smaller than convergence_threshold
50
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
51
+ break
52
+ -
53
+ +
54
+ return np.maximum(radii, 0)
55
+
56
+
57
+ -class LocalSearchRefiner:
58
+ - """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
59
+ - def __init__(self, config):
60
+ +class HybridLocalSearcher:
61
+ + """
62
+ + A powerful local search refiner based on a two-phase physical simulation.
63
+ + This approach incorporates a "growth pressure" concept for exploration.
64
+ + """
65
+ + def __init__(self, n, config):
66
+ + self.n = n
67
+ self.config = config
68
+
69
+ + def _compute_radii_for_sim(self, centers):
70
+ + """A fast radius computation for use inside the simulation loop."""
71
+ + return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
72
+ +
73
+ def refine(self, centers):
74
+ - """Applies a two-stage force-directed refinement to polish a solution."""
75
+ + """Applies a two-phase physical simulation to refine a configuration."""
76
+ refined_centers = centers.copy()
77
+
78
+ - # --- Stage 1: Aggressive settling with subtle growth pressure ---
79
+ - for i_agg_iter in range(self.config['ls_aggressive_iter']):
80
+ - progress = i_agg_iter / self.config['ls_aggressive_iter']
81
+ - current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
82
+ - (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
83
+ - (1.0 - progress)**1.5 # Cubic decay for growth pressure
84
+ -
85
+ - radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
86
+ - pressured_radii = radii * current_growth_pressure
87
+ -
88
+ - # Vectorized Force Calculation
89
+ + # Phase 1: Main refinement with decreasing growth pressure
90
+ + iterations = self.config['sim_iter']
91
+ + base_lr = self.config['learning_rate']
92
+ + wall_strength = self.config['wall_strength']
93
+ + initial_growth_pressure = self.config['initial_growth_pressure']
94
+ + final_growth_pressure = self.config['final_growth_pressure']
95
+ +
96
+ + for i_iter in range(iterations):
97
+ + progress = i_iter / iterations
98
+ + # Anneal growth pressure quadratically
99
+ + current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
100
+ + current_radii = self._compute_radii_for_sim(refined_centers)
101
+ + pressured_radii = current_radii * current_growth_pressure
102
+ +
103
+ + # Vectorized force calculations
104
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
105
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
106
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
107
+ -
108
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
109
+ overlaps = np.maximum(0, radii_sums - dists)
110
+ np.fill_diagonal(overlaps, 0)
111
+ -
112
+ - force_mags = overlaps
113
+ - force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
114
+ + force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
115
+ circle_forces = np.sum(force_matrix, axis=1)
116
+
117
+ wall_forces = np.zeros_like(refined_centers)
118
+ - wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
119
+ - wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
120
+ - wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
121
+ - wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
122
+ -
123
+ + # Wall forces calculation
124
+ + wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
125
+ + wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
126
+ + wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
127
+ + wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
128
+ +
129
+ forces = circle_forces + wall_forces
130
+ -
131
+ - current_lr_agg = self.config['ls_aggressive_lr_end'] + \
132
+ - (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
133
+ - (1.0 - progress)**2.0 # Quadratic decay for LR
134
+ -
135
+ - refined_centers += forces * current_lr_agg
136
+ + # Anneal learning rate quadratically
137
+ + lr = base_lr * (1.0 - progress)**2
138
+ + refined_centers += forces * lr
139
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
140
+
141
+ - # --- Stage 2: Fine-tuning without growth pressure ---
142
+ - for _ in range(self.config['ls_fine_tune_iter']):
143
+ - radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
144
+ -
145
+ - # Vectorized Force Calculation (no growth pressure)
146
+ + # Phase 2: Fine-tuning with minimal growth pressure
147
+ + iterations_ft = self.config['fine_tune_iter']
148
+ + lr_ft = self.config['fine_tune_lr']
149
+ + for _ in range(iterations_ft):
150
+ + current_radii = self._compute_radii_for_sim(refined_centers)
151
+ +
152
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
153
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
154
+ dists[dists < 1e-9] = 1e-9
155
+ -
156
+ - radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
157
+ + radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
158
+ overlaps = np.maximum(0, radii_sums - dists)
159
+ np.fill_diagonal(overlaps, 0)
160
+ -
161
+ - force_mags = overlaps
162
+ - force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
163
+ + force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
164
+ circle_forces = np.sum(force_matrix, axis=1)
165
+
166
+ wall_forces = np.zeros_like(refined_centers)
167
+ - wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
168
+ - wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
169
+ - wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
170
+ - wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
171
+ -
172
+ + # Wall forces calculation
173
+ + wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
174
+ + wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
175
+ + wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
176
+ + wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
177
+ +
178
+ forces = circle_forces + wall_forces
179
+ -
180
+ - refined_centers += forces * self.config['ls_fine_tune_lr']
181
+ + refined_centers += forces * lr_ft
182
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
183
+
184
+ return refined_centers
185
+
186
+
187
+ class MemeticAlgorithm:
188
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
189
+ def __init__(self, n, config):
190
+ self.n = n
191
+ self.config = config
192
+ self.population = []
193
+ self.fitnesses = np.array([])
194
+ self.best_solution = None
195
+ self.best_fitness = -1.0
196
+ - self.local_search_refiner = LocalSearchRefiner(config=config)
197
+ + # Use the HybridLocalSearcher with its specific config
198
+ + self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
199
+
200
+ def _initialize_population(self):
201
+ - """Initializes a diverse population with both random and grid-based individuals."""
202
+ - num_grid_based = self.config['population_size'] // 4
203
+ - num_cells_side = 5
204
+ - spacing = 1.0 / num_cells_side
205
+ - interstitial_points = [
206
+ - [spacing, spacing], [spacing, 1 - spacing],
207
+ - [1 - spacing, spacing], [1 - spacing, 1 - spacing]
208
+ - ]
209
+ -
210
+ - for i in range(self.config['population_size']):
211
+ - if i < num_grid_based:
212
+ - centers = np.zeros((self.n, 2))
213
+ - k = 0
214
+ - for row in range(num_cells_side):
215
+ - for col in range(num_cells_side):
216
+ - centers[k, 0] = (col + 0.5) * spacing
217
+ - centers[k, 1] = (row + 0.5) * spacing
218
+ - k += 1
219
+ - centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
220
+ -
221
+ - # Place 26th circle in one of the strategic voids
222
+ - extra_pos = interstitial_points[i % len(interstitial_points)]
223
+ - centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
224
+ - self.population.append(np.clip(centers, 0.0, 1.0))
225
+ - else:
226
+ - self.population.append(np.random.rand(self.n, 2))
227
+ -
228
+ + """Initializes a diverse population using strategic grid-based starts and random individuals."""
229
+ + spacing = 1.0 / 5
230
+ + # The explicit list of candidate positions for the 26th circle, from a high-performing prior
231
+ + candidate_extra_positions = self.config['initial_candidates']
232
+ +
233
+ + # Base for 25 circles in a 5x5 grid, to be perturbed
234
+ + base_centers_25 = np.zeros((self.n - 1, 2))
235
+ + k = 0
236
+ + for j in range(5):
237
+ + for i in range(5):
238
+ + base_centers_25[k, 0] = (i + 0.5) * spacing
239
+ + base_centers_25[k, 1] = (j + 0.5) * spacing
240
+ + k += 1
241
+ +
242
+ + num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
243
+ +
244
+ + # Add strategically initialized configurations
245
+ + for i in range(num_strategic_starts):
246
+ + centers = np.zeros((self.n, 2))
247
+ + # Apply perturbation to break symmetry
248
+ + perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers_25.shape)
249
+ + perturbed_base_centers = np.clip(base_centers_25 + perturbation, 0.0, 1.0)
250
+ + centers[:self.n-1] = perturbed_base_centers
251
+ + # Place the 26th circle at a strategic point
252
+ + centers[self.n-1] = np.array(candidate_extra_positions[i])
253
+ + self.population.append(centers)
254
+ +
255
+ + # Add purely random configurations to ensure broad exploration
256
+ + num_random_starts = self.config['population_size'] - len(self.population)
257
+ + for _ in range(num_random_starts):
258
+ + self.population.append(np.random.rand(self.n, 2))
259
+ +
260
+ def _evaluate_population(self):
261
+ """Calculates fitness for the population and updates the best solution."""
262
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
263
+ best_idx = np.argmax(self.fitnesses)
264
+ if self.fitnesses[best_idx] > self.best_fitness:
265
+ self.best_fitness = self.fitnesses[best_idx]
266
+ self.best_solution = self.population[best_idx].copy()
267
+ -
268
+ +
269
+ def _select_parent(self):
270
+ """Selects a parent using tournament selection."""
271
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
272
+ tourn_fitnesses = self.fitnesses[tourn_indices]
273
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
274
+ return self.population[winner_idx]
275
+
276
+ def _crossover(self, p1, p2):
277
+ - """
278
+ - Performs Blend Crossover (BLX-alpha) on continuous variables.
279
+ - Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
280
+ - """
281
+ - # Using a small random range around 0.5 for alpha for averaging blend
282
+ - alpha = np.random.uniform(0.4, 0.6)
283
+ - child = alpha * p1 + (1 - alpha) * p2
284
+ - return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
285
+ + """Performs uniform crossover, taking whole circles from either parent."""
286
+ + child = p1.copy()
287
+ + mask = np.random.rand(self.n) < 0.5
288
+ + child[mask] = p2[mask]
289
+ + return child
290
+
291
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
292
+ - """Applies Gaussian mutation and a chance of a strong mutation."""
293
+ + """Applies Gaussian mutation and a chance of a strong 'jump' mutation."""
294
+ mutated_ind = individual.copy()
295
+ for i in range(self.n):
296
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
297
+ noise = np.random.normal(0, strength, size=2)
298
+ mutated_ind[i] += noise
299
+ -
300
+ - if np.random.rand() < 0.02: # 2% chance of a jump mutation
301
+ +
302
+ + if np.random.rand() < self.config['jump_mutation_prob']: # Jump mutation probability from a good prior
303
+ idx_to_reset = np.random.randint(0, self.n)
304
+ mutated_ind[idx_to_reset] = np.random.rand(2)
305
+
306
+ return np.clip(mutated_ind, 0.0, 1.0)
307
+
308
+ def run(self):
309
+ """Executes the full memetic algorithm evolution."""
310
+ self._initialize_population()
311
+ -
312
+ +
313
+ for gen in range(self.config['generations']):
314
+ self._evaluate_population()
315
+ -
316
+ +
317
+ new_population = []
318
+ -
319
+ - # Elitism
320
+ +
321
+ + # Elitism: carry over the best individuals directly
322
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
323
+ for idx in elite_indices:
324
+ new_population.append(self.population[idx].copy())
325
+ -
326
+ - # Anneal mutation strength
327
+ +
328
+ + # Anneal mutation strength over generations with a quadratic decay
329
+ mut_strength = self.config['mut_strength_end'] + \
330
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
331
+ - (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
332
+ -
333
+ - current_mutation_rate = self.config['mutation_rate_end'] + \
334
+ - (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
335
+ - (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
336
+ + (1.0 - (gen / self.config['generations']))**2.0
337
+ +
338
+ + # Anneal mutation rate per circle over generations with a cubic decay
339
+ + current_mutation_rate_per_circle = self.config['mutation_rate_end_per_circle'] + \
340
+ + (self.config['mutation_rate_start_per_circle'] - self.config['mutation_rate_end_per_circle']) * \
341
+ + (1.0 - (gen / self.config['generations']))**1.5
342
+
343
+ while len(new_population) < self.config['population_size']:
344
+ p1 = self._select_parent()
345
+ p2 = self._select_parent()
346
+ -
347
+ +
348
+ + # Perform crossover, or just copy a parent if crossover doesn't happen
349
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
350
+ - child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
351
+ -
352
+ - # Memetic step: Apply local search probabilistically
353
+ + # Apply mutation with annealed strength and rate
354
+ + child = self._mutate(child, mut_strength, current_mutation_rate_per_circle)
355
+ +
356
+ + # Memetic step: Apply powerful local search probabilistically
357
+ if np.random.rand() < self.config['local_search_prob']:
358
+ child = self.local_search_refiner.refine(child)
359
+ -
360
+ +
361
+ new_population.append(child)
362
+ -
363
+ +
364
+ self.population = new_population
365
+ -
366
+ +
367
+ # Final evaluation to get the best of the last generation
368
+ self._evaluate_population()
369
+ return self.best_solution
370
+
371
+
372
+ def construct_packing():
373
+ """
374
+ Constructs a packing of 26 circles using a Memetic Algorithm.
375
+ """
376
+ n = 26
377
+ + spacing = 1.0 / 5
378
+ config = {
379
+ + # MA parameters
380
+ 'population_size': 80, # Increased population size for more diversity
381
+ - 'generations': 400, # Increased generations for more thorough evolution
382
+ - 'elite_count': 4,
383
+ - 'tournament_size': 7, # Increased tournament size for stronger selection pressure
384
+ - 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
385
+ - 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
386
+ - 'mut_strength_start': 0.1,
387
+ - 'mut_strength_end': 0.001,
388
+ - 'crossover_rate': 0.9,
389
+ - # Memetic Parameters
390
+ - 'local_search_prob': 0.15, # Probability of applying local search to a new child
391
+ -
392
+ - # Parameters for the two-stage Local Search Refiner (Recommendation #4)
393
+ - 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
394
+ - 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
395
+ - 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
396
+ - 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
397
+ - 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
398
+ - 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
399
+ -
400
+ - 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
401
+ - 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
402
+ - 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
403
+ - 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
404
+ + 'generations': 350, # Sufficient generations for thorough evolution
405
+ + 'elite_count': 5, # Number of elite individuals to carry over
406
+ + 'tournament_size': 6, # Tournament size for selection pressure
407
+ + 'mutation_rate_start_per_circle': 0.45, # Initial probability for a circle to mutate
408
+ + 'mutation_rate_end_per_circle': 0.05, # Final probability after annealing
409
+ + 'mut_strength_start': 0.1, # Initial strength of Gaussian mutation
410
+ + 'mut_strength_end': 0.001, # Final strength of Gaussian mutation
411
+ + 'crossover_rate': 0.9, # Probability of performing crossover
412
+ + 'jump_mutation_prob': 0.03, # Probability of a strong "jump" mutation for a circle
413
+ +
414
+ + # Initialization parameters for _initialize_population
415
+ + 'initial_perturbation_scale': 0.02, # Scale for perturbing grid-based initial positions
416
+ + 'initial_candidates': [ # Strategic points for the 26th circle
417
+ + [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
418
+ + [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
419
+ + [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
420
+ + [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
421
+ + ],
422
+ +
423
+ + # Memetic Parameters (Local Search, passed to HybridLocalSearcher)
424
+ + 'local_search_prob': 0.20, # Probability of applying local search to a new child
425
+ + 'ls_config': { # Configuration for the HybridLocalSearcher
426
+ + 'sim_iter': 500, # Iterations for the main simulation phase
427
+ + 'radius_sim_iter': 150, # Radii calculation iterations during simulation
428
+ + 'learning_rate': 0.018, # Base learning rate for center updates
429
+ + 'wall_strength': 0.65, # Strength of repulsion from square walls
430
+ + 'initial_growth_pressure': 1.05, # Initial multiplier for radii to create expansion pressure
431
+ + 'final_growth_pressure': 1.001, # Final multiplier for radii
432
+ + 'fine_tune_iter': 250, # Iterations for the fine-tuning phase
433
+ + 'fine_tune_lr': 0.0015, # Learning rate for the fine-tuning phase
434
+ + }
435
+ }
436
+
437
+ solver = MemeticAlgorithm(n=n, config=config)
438
+ best_centers = solver.run()
439
+ -
440
+ +
441
+ # Final, high-precision radius calculation for the best solution
442
+ - final_radii = compute_max_radii(best_centers, max_iter=1500)
443
+ -
444
+ + final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
445
+ +
446
+ return best_centers, final_radii
447
+ -
448
+ # EVOLVE-BLOCK-END
449
+
450
+
451
+ # This part remains fixed (not evolved)
452
+ def run_packing():
453
+ """Run the circle packing constructor for n=26"""
454
+ centers, radii = construct_packing()
455
+ # Calculate the sum of radii
456
+ sum_radii = np.sum(radii)
457
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/main.py ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+
4
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
5
+ """
6
+ Compute maximum radii using iterative proportional scaling with a convergence check.
7
+ """
8
+ n = centers.shape[0]
9
+ # Initialize radii to be constrained by walls
10
+ radii = np.min([
11
+ centers[:, 0], 1 - centers[:, 0],
12
+ centers[:, 1], 1 - centers[:, 1]
13
+ ], axis=0)
14
+
15
+ for _ in range(max_iter):
16
+ old_radii = radii.copy()
17
+ updated = False
18
+ # Calculate distances and handle overlaps for all pairs
19
+ # Vectorized calculation for distances to improve performance
20
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
21
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
22
+
23
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
24
+ for i in range(n):
25
+ for j in range(i + 1, n):
26
+ dist = dists[i, j] # Pre-calculated distance
27
+
28
+ if radii[i] + radii[j] > dist:
29
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
30
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
31
+ radii[i], radii[j] = 0.0, 0.0
32
+ updated = True
33
+ else:
34
+ scale = dist / (radii[i] + radii[j])
35
+ radii[i] *= scale
36
+ radii[j] *= scale
37
+ updated = True
38
+
39
+ # Check for convergence: if no updates were made AND the max change is tiny.
40
+ # This prevents infinite loops if changes are smaller than convergence_threshold
41
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
42
+ break
43
+
44
+ return np.maximum(radii, 0)
45
+
46
+
47
+ class HybridLocalSearcher:
48
+ """
49
+ A powerful local search refiner based on a two-phase physical simulation.
50
+ This approach incorporates a "growth pressure" concept for exploration.
51
+ """
52
+ def __init__(self, n, config):
53
+ self.n = n
54
+ self.config = config
55
+
56
+ def _compute_radii_for_sim(self, centers):
57
+ """A fast radius computation for use inside the simulation loop."""
58
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
59
+
60
+ def refine(self, centers):
61
+ """Applies a two-phase physical simulation to refine a configuration."""
62
+ refined_centers = centers.copy()
63
+
64
+ # Phase 1: Main refinement with decreasing growth pressure
65
+ iterations = self.config['sim_iter']
66
+ base_lr = self.config['learning_rate']
67
+ wall_strength = self.config['wall_strength']
68
+ initial_growth_pressure = self.config['initial_growth_pressure']
69
+ final_growth_pressure = self.config['final_growth_pressure']
70
+
71
+ for i_iter in range(iterations):
72
+ progress = i_iter / iterations
73
+ # Anneal growth pressure quadratically
74
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
75
+ current_radii = self._compute_radii_for_sim(refined_centers)
76
+ pressured_radii = current_radii * current_growth_pressure
77
+
78
+ # Vectorized force calculations
79
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
80
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
81
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
82
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
83
+ overlaps = np.maximum(0, radii_sums - dists)
84
+ np.fill_diagonal(overlaps, 0)
85
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
86
+ circle_forces = np.sum(force_matrix, axis=1)
87
+
88
+ wall_forces = np.zeros_like(refined_centers)
89
+ # Wall forces calculation
90
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
91
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
92
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
93
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
94
+
95
+ forces = circle_forces + wall_forces
96
+ # Anneal learning rate quadratically
97
+ lr = base_lr * (1.0 - progress)**2
98
+ refined_centers += forces * lr
99
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
100
+
101
+ # Phase 2: Fine-tuning with minimal growth pressure
102
+ iterations_ft = self.config['fine_tune_iter']
103
+ lr_ft = self.config['fine_tune_lr']
104
+ for _ in range(iterations_ft):
105
+ current_radii = self._compute_radii_for_sim(refined_centers)
106
+
107
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
108
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
109
+ dists[dists < 1e-9] = 1e-9
110
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
111
+ overlaps = np.maximum(0, radii_sums - dists)
112
+ np.fill_diagonal(overlaps, 0)
113
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
114
+ circle_forces = np.sum(force_matrix, axis=1)
115
+
116
+ wall_forces = np.zeros_like(refined_centers)
117
+ # Wall forces calculation
118
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
119
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
120
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
121
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
122
+
123
+ forces = circle_forces + wall_forces
124
+ refined_centers += forces * lr_ft
125
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
126
+
127
+ return refined_centers
128
+
129
+
130
+ class MemeticAlgorithm:
131
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
132
+ def __init__(self, n, config):
133
+ self.n = n
134
+ self.config = config
135
+ self.population = []
136
+ self.fitnesses = np.array([])
137
+ self.best_solution = None
138
+ self.best_fitness = -1.0
139
+ # Use the HybridLocalSearcher with its specific config
140
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
141
+
142
+ def _initialize_population(self):
143
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
144
+ spacing = 1.0 / 5
145
+ # The explicit list of candidate positions for the 26th circle, from a high-performing prior
146
+ candidate_extra_positions = self.config['initial_candidates']
147
+
148
+ # Base for 25 circles in a 5x5 grid, to be perturbed
149
+ base_centers_25 = np.zeros((self.n - 1, 2))
150
+ k = 0
151
+ for j in range(5):
152
+ for i in range(5):
153
+ base_centers_25[k, 0] = (i + 0.5) * spacing
154
+ base_centers_25[k, 1] = (j + 0.5) * spacing
155
+ k += 1
156
+
157
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
158
+
159
+ # Add strategically initialized configurations
160
+ for i in range(num_strategic_starts):
161
+ centers = np.zeros((self.n, 2))
162
+ # Apply perturbation to break symmetry
163
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers_25.shape)
164
+ perturbed_base_centers = np.clip(base_centers_25 + perturbation, 0.0, 1.0)
165
+ centers[:self.n-1] = perturbed_base_centers
166
+ # Place the 26th circle at a strategic point
167
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
168
+ self.population.append(centers)
169
+
170
+ # Add purely random configurations to ensure broad exploration
171
+ num_random_starts = self.config['population_size'] - len(self.population)
172
+ for _ in range(num_random_starts):
173
+ self.population.append(np.random.rand(self.n, 2))
174
+
175
+ def _evaluate_population(self):
176
+ """Calculates fitness for the population and updates the best solution."""
177
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
178
+ best_idx = np.argmax(self.fitnesses)
179
+ if self.fitnesses[best_idx] > self.best_fitness:
180
+ self.best_fitness = self.fitnesses[best_idx]
181
+ self.best_solution = self.population[best_idx].copy()
182
+
183
+ def _select_parent(self):
184
+ """Selects a parent using tournament selection."""
185
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
186
+ tourn_fitnesses = self.fitnesses[tourn_indices]
187
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
188
+ return self.population[winner_idx]
189
+
190
+ def _crossover(self, p1, p2):
191
+ """Performs uniform crossover, taking whole circles from either parent."""
192
+ child = p1.copy()
193
+ mask = np.random.rand(self.n) < 0.5
194
+ child[mask] = p2[mask]
195
+ return child
196
+
197
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
198
+ """Applies Gaussian mutation and a chance of a strong 'jump' mutation."""
199
+ mutated_ind = individual.copy()
200
+ for i in range(self.n):
201
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
202
+ noise = np.random.normal(0, strength, size=2)
203
+ mutated_ind[i] += noise
204
+
205
+ if np.random.rand() < self.config['jump_mutation_prob']: # Jump mutation probability from a good prior
206
+ idx_to_reset = np.random.randint(0, self.n)
207
+ mutated_ind[idx_to_reset] = np.random.rand(2)
208
+
209
+ return np.clip(mutated_ind, 0.0, 1.0)
210
+
211
+ def run(self):
212
+ """Executes the full memetic algorithm evolution."""
213
+ self._initialize_population()
214
+
215
+ for gen in range(self.config['generations']):
216
+ self._evaluate_population()
217
+
218
+ new_population = []
219
+
220
+ # Elitism: carry over the best individuals directly
221
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
222
+ for idx in elite_indices:
223
+ new_population.append(self.population[idx].copy())
224
+
225
+ # Anneal mutation strength over generations with a quadratic decay
226
+ mut_strength = self.config['mut_strength_end'] + \
227
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
228
+ (1.0 - (gen / self.config['generations']))**2.0
229
+
230
+ # Anneal mutation rate per circle over generations with a cubic decay
231
+ current_mutation_rate_per_circle = self.config['mutation_rate_end_per_circle'] + \
232
+ (self.config['mutation_rate_start_per_circle'] - self.config['mutation_rate_end_per_circle']) * \
233
+ (1.0 - (gen / self.config['generations']))**1.5
234
+
235
+ while len(new_population) < self.config['population_size']:
236
+ p1 = self._select_parent()
237
+ p2 = self._select_parent()
238
+
239
+ # Perform crossover, or just copy a parent if crossover doesn't happen
240
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
241
+ # Apply mutation with annealed strength and rate
242
+ child = self._mutate(child, mut_strength, current_mutation_rate_per_circle)
243
+
244
+ # Memetic step: Apply powerful local search probabilistically
245
+ if np.random.rand() < self.config['local_search_prob']:
246
+ child = self.local_search_refiner.refine(child)
247
+
248
+ new_population.append(child)
249
+
250
+ self.population = new_population
251
+
252
+ # Final evaluation to get the best of the last generation
253
+ self._evaluate_population()
254
+ return self.best_solution
255
+
256
+
257
+ def construct_packing():
258
+ """
259
+ Constructs a packing of 26 circles using a Memetic Algorithm.
260
+ """
261
+ n = 26
262
+ spacing = 1.0 / 5
263
+ config = {
264
+ # MA parameters
265
+ 'population_size': 80, # Increased population size for more diversity
266
+ 'generations': 350, # Sufficient generations for thorough evolution
267
+ 'elite_count': 5, # Number of elite individuals to carry over
268
+ 'tournament_size': 6, # Tournament size for selection pressure
269
+ 'mutation_rate_start_per_circle': 0.45, # Initial probability for a circle to mutate
270
+ 'mutation_rate_end_per_circle': 0.05, # Final probability after annealing
271
+ 'mut_strength_start': 0.1, # Initial strength of Gaussian mutation
272
+ 'mut_strength_end': 0.001, # Final strength of Gaussian mutation
273
+ 'crossover_rate': 0.9, # Probability of performing crossover
274
+ 'jump_mutation_prob': 0.03, # Probability of a strong "jump" mutation for a circle
275
+
276
+ # Initialization parameters for _initialize_population
277
+ 'initial_perturbation_scale': 0.02, # Scale for perturbing grid-based initial positions
278
+ 'initial_candidates': [ # Strategic points for the 26th circle
279
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
280
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
281
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
282
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
283
+ ],
284
+
285
+ # Memetic Parameters (Local Search, passed to HybridLocalSearcher)
286
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
287
+ 'ls_config': { # Configuration for the HybridLocalSearcher
288
+ 'sim_iter': 500, # Iterations for the main simulation phase
289
+ 'radius_sim_iter': 150, # Radii calculation iterations during simulation
290
+ 'learning_rate': 0.018, # Base learning rate for center updates
291
+ 'wall_strength': 0.65, # Strength of repulsion from square walls
292
+ 'initial_growth_pressure': 1.05, # Initial multiplier for radii to create expansion pressure
293
+ 'final_growth_pressure': 1.001, # Final multiplier for radii
294
+ 'fine_tune_iter': 250, # Iterations for the fine-tuning phase
295
+ 'fine_tune_lr': 0.0015, # Learning rate for the fine-tuning phase
296
+ }
297
+ }
298
+
299
+ solver = MemeticAlgorithm(n=n, config=config)
300
+ best_centers = solver.run()
301
+
302
+ # Final, high-precision radius calculation for the best solution
303
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
304
+
305
+ return best_centers, final_radii
306
+ # EVOLVE-BLOCK-END
307
+
308
+
309
+ # This part remains fixed (not evolved)
310
+ def run_packing():
311
+ """Run the circle packing constructor for n=26"""
312
+ centers, radii = construct_packing()
313
+ # Calculate the sum of radii
314
+ sum_radii = np.sum(radii)
315
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/original.py ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+
4
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
5
+ """
6
+ Compute maximum radii using iterative proportional scaling with a convergence check.
7
+ """
8
+ n = centers.shape[0]
9
+ radii = np.min([
10
+ centers[:, 0], 1 - centers[:, 0],
11
+ centers[:, 1], 1 - centers[:, 1]
12
+ ], axis=0)
13
+
14
+ for _ in range(max_iter):
15
+ old_radii = radii.copy()
16
+ updated = False
17
+ for i in range(n):
18
+ for j in range(i + 1, n):
19
+ dist = np.linalg.norm(centers[i] - centers[j])
20
+ if radii[i] + radii[j] > dist:
21
+ if dist < 1e-9:
22
+ radii[i], radii[j] = 0.0, 0.0
23
+ else:
24
+ scale = dist / (radii[i] + radii[j])
25
+ radii[i] *= scale
26
+ radii[j] *= scale
27
+ updated = True
28
+
29
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
30
+ break
31
+
32
+ return np.maximum(radii, 0)
33
+
34
+
35
+ class LocalSearchRefiner:
36
+ """A class to perform fast, local refinement on a set of centers using a two-stage approach."""
37
+ def __init__(self, config):
38
+ self.config = config
39
+
40
+ def refine(self, centers):
41
+ """Applies a two-stage force-directed refinement to polish a solution."""
42
+ refined_centers = centers.copy()
43
+
44
+ # --- Stage 1: Aggressive settling with subtle growth pressure ---
45
+ for i_agg_iter in range(self.config['ls_aggressive_iter']):
46
+ progress = i_agg_iter / self.config['ls_aggressive_iter']
47
+ current_growth_pressure = self.config['ls_aggressive_growth_pressure_end'] + \
48
+ (self.config['ls_aggressive_growth_pressure_start'] - self.config['ls_aggressive_growth_pressure_end']) * \
49
+ (1.0 - progress)**1.5 # Cubic decay for growth pressure
50
+
51
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_agg'])
52
+ pressured_radii = radii * current_growth_pressure
53
+
54
+ # Vectorized Force Calculation
55
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
56
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
57
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
58
+
59
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
60
+ overlaps = np.maximum(0, radii_sums - dists)
61
+ np.fill_diagonal(overlaps, 0)
62
+
63
+ force_mags = overlaps
64
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
65
+ circle_forces = np.sum(force_matrix, axis=1)
66
+
67
+ wall_forces = np.zeros_like(refined_centers)
68
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 0])
69
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
70
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, pressured_radii - refined_centers[:, 1])
71
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
72
+
73
+ forces = circle_forces + wall_forces
74
+
75
+ current_lr_agg = self.config['ls_aggressive_lr_end'] + \
76
+ (self.config['ls_aggressive_lr_start'] - self.config['ls_aggressive_lr_end']) * \
77
+ (1.0 - progress)**2.0 # Quadratic decay for LR
78
+
79
+ refined_centers += forces * current_lr_agg
80
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
81
+
82
+ # --- Stage 2: Fine-tuning without growth pressure ---
83
+ for _ in range(self.config['ls_fine_tune_iter']):
84
+ radii = compute_max_radii(refined_centers, max_iter=self.config['ls_radius_iter_ft'])
85
+
86
+ # Vectorized Force Calculation (no growth pressure)
87
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
88
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
89
+ dists[dists < 1e-9] = 1e-9
90
+
91
+ radii_sums = radii[:, np.newaxis] + radii[np.newaxis, :]
92
+ overlaps = np.maximum(0, radii_sums - dists)
93
+ np.fill_diagonal(overlaps, 0)
94
+
95
+ force_mags = overlaps
96
+ force_matrix = diffs * (force_mags / dists)[:, :, np.newaxis]
97
+ circle_forces = np.sum(force_matrix, axis=1)
98
+
99
+ wall_forces = np.zeros_like(refined_centers)
100
+ wall_forces[:, 0] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 0])
101
+ wall_forces[:, 0] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 0] + radii) - 1.0)
102
+ wall_forces[:, 1] += self.config['ls_wall_strength'] * np.maximum(0, radii - refined_centers[:, 1])
103
+ wall_forces[:, 1] -= self.config['ls_wall_strength'] * np.maximum(0, (refined_centers[:, 1] + radii) - 1.0)
104
+
105
+ forces = circle_forces + wall_forces
106
+
107
+ refined_centers += forces * self.config['ls_fine_tune_lr']
108
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
109
+
110
+ return refined_centers
111
+
112
+
113
+ class MemeticAlgorithm:
114
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
115
+ def __init__(self, n, config):
116
+ self.n = n
117
+ self.config = config
118
+ self.population = []
119
+ self.fitnesses = np.array([])
120
+ self.best_solution = None
121
+ self.best_fitness = -1.0
122
+ self.local_search_refiner = LocalSearchRefiner(config=config)
123
+
124
+ def _initialize_population(self):
125
+ """Initializes a diverse population with both random and grid-based individuals."""
126
+ num_grid_based = self.config['population_size'] // 4
127
+ num_cells_side = 5
128
+ spacing = 1.0 / num_cells_side
129
+ interstitial_points = [
130
+ [spacing, spacing], [spacing, 1 - spacing],
131
+ [1 - spacing, spacing], [1 - spacing, 1 - spacing]
132
+ ]
133
+
134
+ for i in range(self.config['population_size']):
135
+ if i < num_grid_based:
136
+ centers = np.zeros((self.n, 2))
137
+ k = 0
138
+ for row in range(num_cells_side):
139
+ for col in range(num_cells_side):
140
+ centers[k, 0] = (col + 0.5) * spacing
141
+ centers[k, 1] = (row + 0.5) * spacing
142
+ k += 1
143
+ centers[:25, :] += np.random.normal(0, spacing * 0.01, size=(25, 2))
144
+
145
+ # Place 26th circle in one of the strategic voids
146
+ extra_pos = interstitial_points[i % len(interstitial_points)]
147
+ centers[25] = extra_pos + np.random.normal(0, spacing * 0.01, size=2)
148
+ self.population.append(np.clip(centers, 0.0, 1.0))
149
+ else:
150
+ self.population.append(np.random.rand(self.n, 2))
151
+
152
+ def _evaluate_population(self):
153
+ """Calculates fitness for the population and updates the best solution."""
154
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
155
+ best_idx = np.argmax(self.fitnesses)
156
+ if self.fitnesses[best_idx] > self.best_fitness:
157
+ self.best_fitness = self.fitnesses[best_idx]
158
+ self.best_solution = self.population[best_idx].copy()
159
+
160
+ def _select_parent(self):
161
+ """Selects a parent using tournament selection."""
162
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
163
+ tourn_fitnesses = self.fitnesses[tourn_indices]
164
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
165
+ return self.population[winner_idx]
166
+
167
+ def _crossover(self, p1, p2):
168
+ """
169
+ Performs Blend Crossover (BLX-alpha) on continuous variables.
170
+ Child = alpha * Parent1 + (1-alpha) * Parent2, where alpha is randomly chosen.
171
+ """
172
+ # Using a small random range around 0.5 for alpha for averaging blend
173
+ alpha = np.random.uniform(0.4, 0.6)
174
+ child = alpha * p1 + (1 - alpha) * p2
175
+ return np.clip(child, 0.0, 1.0) # Ensure coordinates stay within bounds
176
+
177
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
178
+ """Applies Gaussian mutation and a chance of a strong mutation."""
179
+ mutated_ind = individual.copy()
180
+ for i in range(self.n):
181
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
182
+ noise = np.random.normal(0, strength, size=2)
183
+ mutated_ind[i] += noise
184
+
185
+ if np.random.rand() < 0.02: # 2% chance of a jump mutation
186
+ idx_to_reset = np.random.randint(0, self.n)
187
+ mutated_ind[idx_to_reset] = np.random.rand(2)
188
+
189
+ return np.clip(mutated_ind, 0.0, 1.0)
190
+
191
+ def run(self):
192
+ """Executes the full memetic algorithm evolution."""
193
+ self._initialize_population()
194
+
195
+ for gen in range(self.config['generations']):
196
+ self._evaluate_population()
197
+
198
+ new_population = []
199
+
200
+ # Elitism
201
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
202
+ for idx in elite_indices:
203
+ new_population.append(self.population[idx].copy())
204
+
205
+ # Anneal mutation strength
206
+ mut_strength = self.config['mut_strength_end'] + \
207
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
208
+ (1.0 - (gen / self.config['generations']))**2.0 # Cubic decay for strength
209
+
210
+ current_mutation_rate = self.config['mutation_rate_end'] + \
211
+ (self.config['mutation_rate'] - self.config['mutation_rate_end']) * \
212
+ (1.0 - (gen / self.config['generations']))**1.5 # Quadratic decay for rate (Recommendation #5)
213
+
214
+ while len(new_population) < self.config['population_size']:
215
+ p1 = self._select_parent()
216
+ p2 = self._select_parent()
217
+
218
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
219
+ child = self._mutate(child, mut_strength, current_mutation_rate) # Pass current_mutation_rate
220
+
221
+ # Memetic step: Apply local search probabilistically
222
+ if np.random.rand() < self.config['local_search_prob']:
223
+ child = self.local_search_refiner.refine(child)
224
+
225
+ new_population.append(child)
226
+
227
+ self.population = new_population
228
+
229
+ # Final evaluation to get the best of the last generation
230
+ self._evaluate_population()
231
+ return self.best_solution
232
+
233
+
234
+ def construct_packing():
235
+ """
236
+ Constructs a packing of 26 circles using a Memetic Algorithm.
237
+ """
238
+ n = 26
239
+ config = {
240
+ 'population_size': 80, # Increased population size for more diversity
241
+ 'generations': 400, # Increased generations for more thorough evolution
242
+ 'elite_count': 4,
243
+ 'tournament_size': 7, # Increased tournament size for stronger selection pressure
244
+ 'mutation_rate': 0.25, # Initial mutation rate (probability for a circle to mutate)
245
+ 'mutation_rate_end': 0.05, # Final mutation rate after annealing (Recommendation #5)
246
+ 'mut_strength_start': 0.1,
247
+ 'mut_strength_end': 0.001,
248
+ 'crossover_rate': 0.9,
249
+ # Memetic Parameters
250
+ 'local_search_prob': 0.15, # Probability of applying local search to a new child
251
+
252
+ # Parameters for the two-stage Local Search Refiner (Recommendation #4)
253
+ 'ls_aggressive_iter': 25, # Iterations for the first, aggressive local search stage
254
+ 'ls_aggressive_lr_start': 0.005, # Initial learning rate for aggressive stage
255
+ 'ls_aggressive_lr_end': 0.001, # Final learning rate for aggressive stage
256
+ 'ls_aggressive_growth_pressure_start': 1.003, # Subtle initial growth pressure for aggressive stage
257
+ 'ls_aggressive_growth_pressure_end': 1.0001, # Subtle final growth pressure for aggressive stage
258
+ 'ls_radius_iter_agg': 80, # Radii iterations for aggressive stage (faster)
259
+
260
+ 'ls_fine_tune_iter': 50, # Iterations for the second, fine-tuning local search stage
261
+ 'ls_fine_tune_lr': 0.0005, # Fixed learning rate for fine-tuning stage (very low for precision)
262
+ 'ls_radius_iter_ft': 150, # Radii iterations for fine-tuning stage (more precise)
263
+ 'ls_wall_strength': 0.2, # Wall strength remains common for both LS stages
264
+ }
265
+
266
+ solver = MemeticAlgorithm(n=n, config=config)
267
+ best_centers = solver.run()
268
+
269
+ # Final, high-precision radius calculation for the best solution
270
+ final_radii = compute_max_radii(best_centers, max_iter=1500)
271
+
272
+ return best_centers, final_radii
273
+
274
+ # EVOLVE-BLOCK-END
275
+
276
+
277
+ # This part remains fixed (not evolved)
278
+ def run_packing():
279
+ """Run the circle packing constructor for n=26"""
280
+ centers, radii = construct_packing()
281
+ # Calculate the sum of radii
282
+ sum_radii = np.sum(radii)
283
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/correct.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "correct": true,
3
+ "error": null
4
+ }
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/job_log.err ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.11/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
2
+
3
+ All support for the `google.generativeai` package has ended. It will no longer be receiving
4
+ updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
5
+ See README for more details:
6
+
7
+ https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
8
+
9
+ import google.generativeai as genai # type: ignore[import-not-found]
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/job_log.out ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Evaluating program: results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/main.py
2
+ Saving results to: results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results
3
+ Run 1/1 completed in 2045.39 seconds
4
+ Detailed packing data saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/extra.npz
5
+ Visualization saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/packing_viz.png
6
+ Correctness and error status saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/correct.json
7
+ Metrics saved to results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/metrics.json
8
+ Evaluation and Validation completed successfully.
9
+ Metrics:
10
+ combined_score: 2.3900820406051997
11
+ 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}
12
+ private: {'reported_sum_of_radii': 2.3900820406051997}
13
+ visualization_path: results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/packing_viz.png
14
+ execution_time_mean: 2045.3863690267317
15
+ execution_time_std: 0.0
16
+ num_valid_runs: 1
17
+ num_invalid_runs: 0
18
+ all_validation_errors: []
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/metrics.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "combined_score": 2.3900820406051997,
3
+ "public": {
4
+ "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)",
5
+ "num_circles": 26
6
+ },
7
+ "private": {
8
+ "reported_sum_of_radii": 2.3900820406051997
9
+ },
10
+ "visualization_path": "results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/results/packing_viz.png",
11
+ "execution_time_mean": 2045.3863690267317,
12
+ "execution_time_std": 0.0,
13
+ "num_valid_runs": 1,
14
+ "num_invalid_runs": 0,
15
+ "all_validation_errors": []
16
+ }
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_101/rewrite.txt ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+
4
+ def compute_max_radii(centers, max_iter=750, convergence_threshold=1e-8):
5
+ """
6
+ Compute maximum radii using iterative proportional scaling with a convergence check.
7
+ """
8
+ n = centers.shape[0]
9
+ # Initialize radii to be constrained by walls
10
+ radii = np.min([
11
+ centers[:, 0], 1 - centers[:, 0],
12
+ centers[:, 1], 1 - centers[:, 1]
13
+ ], axis=0)
14
+
15
+ for _ in range(max_iter):
16
+ old_radii = radii.copy()
17
+ updated = False
18
+ # Calculate distances and handle overlaps for all pairs
19
+ # Vectorized calculation for distances to improve performance
20
+ diffs = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
21
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
22
+
23
+ # Iterate over upper triangle to avoid duplicate calculations and self-comparison
24
+ for i in range(n):
25
+ for j in range(i + 1, n):
26
+ dist = dists[i, j] # Pre-calculated distance
27
+
28
+ if radii[i] + radii[j] > dist:
29
+ if dist < 1e-9: # Handle co-located centers: radii must be zero
30
+ if radii[i] > 0 or radii[j] > 0: # Only update if they weren't already zero
31
+ radii[i], radii[j] = 0.0, 0.0
32
+ updated = True
33
+ else:
34
+ scale = dist / (radii[i] + radii[j])
35
+ radii[i] *= scale
36
+ radii[j] *= scale
37
+ updated = True
38
+
39
+ # Check for convergence: if no updates were made AND the max change is tiny.
40
+ # This prevents infinite loops if changes are smaller than convergence_threshold
41
+ if not updated or np.max(np.abs(radii - old_radii)) < convergence_threshold:
42
+ break
43
+
44
+ return np.maximum(radii, 0)
45
+
46
+
47
+ class HybridLocalSearcher:
48
+ """
49
+ A powerful local search refiner based on a two-phase physical simulation.
50
+ This approach incorporates a "growth pressure" concept for exploration.
51
+ """
52
+ def __init__(self, n, config):
53
+ self.n = n
54
+ self.config = config
55
+
56
+ def _compute_radii_for_sim(self, centers):
57
+ """A fast radius computation for use inside the simulation loop."""
58
+ return compute_max_radii(centers, max_iter=self.config['radius_sim_iter'], convergence_threshold=1e-7)
59
+
60
+ def refine(self, centers):
61
+ """Applies a two-phase physical simulation to refine a configuration."""
62
+ refined_centers = centers.copy()
63
+
64
+ # Phase 1: Main refinement with decreasing growth pressure
65
+ iterations = self.config['sim_iter']
66
+ base_lr = self.config['learning_rate']
67
+ wall_strength = self.config['wall_strength']
68
+ initial_growth_pressure = self.config['initial_growth_pressure']
69
+ final_growth_pressure = self.config['final_growth_pressure']
70
+
71
+ for i_iter in range(iterations):
72
+ progress = i_iter / iterations
73
+ # Anneal growth pressure quadratically
74
+ current_growth_pressure = final_growth_pressure + (initial_growth_pressure - final_growth_pressure) * (1.0 - progress)**2
75
+ current_radii = self._compute_radii_for_sim(refined_centers)
76
+ pressured_radii = current_radii * current_growth_pressure
77
+
78
+ # Vectorized force calculations
79
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
80
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
81
+ dists[dists < 1e-9] = 1e-9 # Prevent division by zero
82
+ radii_sums = pressured_radii[:, np.newaxis] + pressured_radii[np.newaxis, :]
83
+ overlaps = np.maximum(0, radii_sums - dists)
84
+ np.fill_diagonal(overlaps, 0)
85
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
86
+ circle_forces = np.sum(force_matrix, axis=1)
87
+
88
+ wall_forces = np.zeros_like(refined_centers)
89
+ # Wall forces calculation
90
+ wall_forces[:, 0] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 0])
91
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + pressured_radii) - 1.0)
92
+ wall_forces[:, 1] += wall_strength * np.maximum(0, pressured_radii - refined_centers[:, 1])
93
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + pressured_radii) - 1.0)
94
+
95
+ forces = circle_forces + wall_forces
96
+ # Anneal learning rate quadratically
97
+ lr = base_lr * (1.0 - progress)**2
98
+ refined_centers += forces * lr
99
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
100
+
101
+ # Phase 2: Fine-tuning with minimal growth pressure
102
+ iterations_ft = self.config['fine_tune_iter']
103
+ lr_ft = self.config['fine_tune_lr']
104
+ for _ in range(iterations_ft):
105
+ current_radii = self._compute_radii_for_sim(refined_centers)
106
+
107
+ diffs = refined_centers[:, np.newaxis, :] - refined_centers[np.newaxis, :, :]
108
+ dists = np.sqrt(np.sum(diffs**2, axis=-1))
109
+ dists[dists < 1e-9] = 1e-9
110
+ radii_sums = current_radii[:, np.newaxis] + current_radii[np.newaxis, :]
111
+ overlaps = np.maximum(0, radii_sums - dists)
112
+ np.fill_diagonal(overlaps, 0)
113
+ force_matrix = diffs * (overlaps / dists)[:, :, np.newaxis]
114
+ circle_forces = np.sum(force_matrix, axis=1)
115
+
116
+ wall_forces = np.zeros_like(refined_centers)
117
+ # Wall forces calculation
118
+ wall_forces[:, 0] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 0])
119
+ wall_forces[:, 0] -= wall_strength * np.maximum(0, (refined_centers[:, 0] + current_radii) - 1.0)
120
+ wall_forces[:, 1] += wall_strength * np.maximum(0, current_radii - refined_centers[:, 1])
121
+ wall_forces[:, 1] -= wall_strength * np.maximum(0, (refined_centers[:, 1] + current_radii) - 1.0)
122
+
123
+ forces = circle_forces + wall_forces
124
+ refined_centers += forces * lr_ft
125
+ refined_centers = np.clip(refined_centers, 0.0, 1.0)
126
+
127
+ return refined_centers
128
+
129
+
130
+ class MemeticAlgorithm:
131
+ """Encapsulates the entire Memetic Algorithm for circle packing."""
132
+ def __init__(self, n, config):
133
+ self.n = n
134
+ self.config = config
135
+ self.population = []
136
+ self.fitnesses = np.array([])
137
+ self.best_solution = None
138
+ self.best_fitness = -1.0
139
+ # Use the HybridLocalSearcher with its specific config
140
+ self.local_search_refiner = HybridLocalSearcher(n=n, config=config['ls_config'])
141
+
142
+ def _initialize_population(self):
143
+ """Initializes a diverse population using strategic grid-based starts and random individuals."""
144
+ spacing = 1.0 / 5
145
+ # The explicit list of candidate positions for the 26th circle, from a high-performing prior
146
+ candidate_extra_positions = self.config['initial_candidates']
147
+
148
+ # Base for 25 circles in a 5x5 grid, to be perturbed
149
+ base_centers_25 = np.zeros((self.n - 1, 2))
150
+ k = 0
151
+ for j in range(5):
152
+ for i in range(5):
153
+ base_centers_25[k, 0] = (i + 0.5) * spacing
154
+ base_centers_25[k, 1] = (j + 0.5) * spacing
155
+ k += 1
156
+
157
+ num_strategic_starts = min(len(candidate_extra_positions), self.config['population_size'] // 2)
158
+
159
+ # Add strategically initialized configurations
160
+ for i in range(num_strategic_starts):
161
+ centers = np.zeros((self.n, 2))
162
+ # Apply perturbation to break symmetry
163
+ perturbation = np.random.normal(0, self.config['initial_perturbation_scale'], size=base_centers_25.shape)
164
+ perturbed_base_centers = np.clip(base_centers_25 + perturbation, 0.0, 1.0)
165
+ centers[:self.n-1] = perturbed_base_centers
166
+ # Place the 26th circle at a strategic point
167
+ centers[self.n-1] = np.array(candidate_extra_positions[i])
168
+ self.population.append(centers)
169
+
170
+ # Add purely random configurations to ensure broad exploration
171
+ num_random_starts = self.config['population_size'] - len(self.population)
172
+ for _ in range(num_random_starts):
173
+ self.population.append(np.random.rand(self.n, 2))
174
+
175
+ def _evaluate_population(self):
176
+ """Calculates fitness for the population and updates the best solution."""
177
+ self.fitnesses = np.array([np.sum(compute_max_radii(ind)) for ind in self.population])
178
+ best_idx = np.argmax(self.fitnesses)
179
+ if self.fitnesses[best_idx] > self.best_fitness:
180
+ self.best_fitness = self.fitnesses[best_idx]
181
+ self.best_solution = self.population[best_idx].copy()
182
+
183
+ def _select_parent(self):
184
+ """Selects a parent using tournament selection."""
185
+ tourn_indices = np.random.choice(len(self.population), self.config['tournament_size'], replace=False)
186
+ tourn_fitnesses = self.fitnesses[tourn_indices]
187
+ winner_idx = tourn_indices[np.argmax(tourn_fitnesses)]
188
+ return self.population[winner_idx]
189
+
190
+ def _crossover(self, p1, p2):
191
+ """Performs uniform crossover, taking whole circles from either parent."""
192
+ child = p1.copy()
193
+ mask = np.random.rand(self.n) < 0.5
194
+ child[mask] = p2[mask]
195
+ return child
196
+
197
+ def _mutate(self, individual, strength, mutation_rate_per_circle):
198
+ """Applies Gaussian mutation and a chance of a strong 'jump' mutation."""
199
+ mutated_ind = individual.copy()
200
+ for i in range(self.n):
201
+ if np.random.rand() < mutation_rate_per_circle: # Use the annealed rate
202
+ noise = np.random.normal(0, strength, size=2)
203
+ mutated_ind[i] += noise
204
+
205
+ if np.random.rand() < self.config['jump_mutation_prob']: # Jump mutation probability from a good prior
206
+ idx_to_reset = np.random.randint(0, self.n)
207
+ mutated_ind[idx_to_reset] = np.random.rand(2)
208
+
209
+ return np.clip(mutated_ind, 0.0, 1.0)
210
+
211
+ def run(self):
212
+ """Executes the full memetic algorithm evolution."""
213
+ self._initialize_population()
214
+
215
+ for gen in range(self.config['generations']):
216
+ self._evaluate_population()
217
+
218
+ new_population = []
219
+
220
+ # Elitism: carry over the best individuals directly
221
+ elite_indices = np.argsort(self.fitnesses)[-self.config['elite_count']:]
222
+ for idx in elite_indices:
223
+ new_population.append(self.population[idx].copy())
224
+
225
+ # Anneal mutation strength over generations with a quadratic decay
226
+ mut_strength = self.config['mut_strength_end'] + \
227
+ (self.config['mut_strength_start'] - self.config['mut_strength_end']) * \
228
+ (1.0 - (gen / self.config['generations']))**2.0
229
+
230
+ # Anneal mutation rate per circle over generations with a cubic decay
231
+ current_mutation_rate_per_circle = self.config['mutation_rate_end_per_circle'] + \
232
+ (self.config['mutation_rate_start_per_circle'] - self.config['mutation_rate_end_per_circle']) * \
233
+ (1.0 - (gen / self.config['generations']))**1.5
234
+
235
+ while len(new_population) < self.config['population_size']:
236
+ p1 = self._select_parent()
237
+ p2 = self._select_parent()
238
+
239
+ # Perform crossover, or just copy a parent if crossover doesn't happen
240
+ child = self._crossover(p1, p2) if np.random.rand() < self.config['crossover_rate'] else p1.copy()
241
+ # Apply mutation with annealed strength and rate
242
+ child = self._mutate(child, mut_strength, current_mutation_rate_per_circle)
243
+
244
+ # Memetic step: Apply powerful local search probabilistically
245
+ if np.random.rand() < self.config['local_search_prob']:
246
+ child = self.local_search_refiner.refine(child)
247
+
248
+ new_population.append(child)
249
+
250
+ self.population = new_population
251
+
252
+ # Final evaluation to get the best of the last generation
253
+ self._evaluate_population()
254
+ return self.best_solution
255
+
256
+
257
+ def construct_packing():
258
+ """
259
+ Constructs a packing of 26 circles using a Memetic Algorithm.
260
+ """
261
+ n = 26
262
+ spacing = 1.0 / 5
263
+ config = {
264
+ # MA parameters
265
+ 'population_size': 80, # Increased population size for more diversity
266
+ 'generations': 350, # Sufficient generations for thorough evolution
267
+ 'elite_count': 5, # Number of elite individuals to carry over
268
+ 'tournament_size': 6, # Tournament size for selection pressure
269
+ 'mutation_rate_start_per_circle': 0.45, # Initial probability for a circle to mutate
270
+ 'mutation_rate_end_per_circle': 0.05, # Final probability after annealing
271
+ 'mut_strength_start': 0.1, # Initial strength of Gaussian mutation
272
+ 'mut_strength_end': 0.001, # Final strength of Gaussian mutation
273
+ 'crossover_rate': 0.9, # Probability of performing crossover
274
+ 'jump_mutation_prob': 0.03, # Probability of a strong "jump" mutation for a circle
275
+
276
+ # Initialization parameters for _initialize_population
277
+ 'initial_perturbation_scale': 0.02, # Scale for perturbing grid-based initial positions
278
+ 'initial_candidates': [ # Strategic points for the 26th circle
279
+ [spacing, spacing], [spacing, 1 - spacing], [1 - spacing, spacing], [1 - spacing, 1 - spacing],
280
+ [0.5, 0.5], [spacing, 0.5], [0.5, spacing], [1 - spacing, 0.5], [0.5, 1 - spacing],
281
+ [0.05, 0.05], [0.05, 0.95], [0.95, 0.05], [0.95, 0.95],
282
+ [0.1, 0.1], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]
283
+ ],
284
+
285
+ # Memetic Parameters (Local Search, passed to HybridLocalSearcher)
286
+ 'local_search_prob': 0.20, # Probability of applying local search to a new child
287
+ 'ls_config': { # Configuration for the HybridLocalSearcher
288
+ 'sim_iter': 500, # Iterations for the main simulation phase
289
+ 'radius_sim_iter': 150, # Radii calculation iterations during simulation
290
+ 'learning_rate': 0.018, # Base learning rate for center updates
291
+ 'wall_strength': 0.65, # Strength of repulsion from square walls
292
+ 'initial_growth_pressure': 1.05, # Initial multiplier for radii to create expansion pressure
293
+ 'final_growth_pressure': 1.001, # Final multiplier for radii
294
+ 'fine_tune_iter': 250, # Iterations for the fine-tuning phase
295
+ 'fine_tune_lr': 0.0015, # Learning rate for the fine-tuning phase
296
+ }
297
+ }
298
+
299
+ solver = MemeticAlgorithm(n=n, config=config)
300
+ best_centers = solver.run()
301
+
302
+ # Final, high-precision radius calculation for the best solution
303
+ final_radii = compute_max_radii(best_centers, max_iter=2000, convergence_threshold=1e-9)
304
+
305
+ return best_centers, final_radii
306
+ # EVOLVE-BLOCK-END
examples_deprecated/circle_packing/results_circle_packing_WITHOUT_vision_20260114_070110/gen_102/__pycache__/main.cpython-311.pyc ADDED
Binary file (18.6 kB). View file