JustinTX commited on
Commit
6b9daca
·
verified ·
1 Parent(s): 1d33d82

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__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/best/edit.diff +297 -0
  2. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/best/main.py +291 -0
  3. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/best/original.py +291 -0
  4. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/best/search_replace.txt +19 -0
  5. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory/EVAL_AGENTS.md +46 -0
  6. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory/aux_metrics.py +98 -0
  7. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory/run_aux_metrics.py +23 -0
  8. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory/service_state.json +212 -0
  9. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory_backup_20260202_200614/EVAL_AGENTS.md +122 -0
  10. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory_backup_20260202_200614/aux_metrics.py +149 -0
  11. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory_backup_20260202_200614/auxiliary_metrics.py +87 -0
  12. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory_backup_20260202_200614/run_aux_metrics_temp.py +14 -0
  13. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory_backup_20260202_200614/service_state.json +128 -0
  14. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_0/main.py +94 -0
  15. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_1/edit.diff +140 -0
  16. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_1/main.py +105 -0
  17. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_1/original.py +94 -0
  18. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_1/search_replace.txt +201 -0
  19. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_10/edit.diff +150 -0
  20. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_10/main.py +96 -0
  21. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_10/original.py +101 -0
  22. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_10/search_replace.txt +186 -0
  23. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_100/edit.diff +364 -0
  24. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_100/main.py +356 -0
  25. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_100/original.py +327 -0
  26. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_100/search_replace.txt +171 -0
  27. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_101/edit.diff +300 -0
  28. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_101/main.py +284 -0
  29. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_101/original.py +237 -0
  30. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_101/search_replace.txt +130 -0
  31. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_102/edit.diff +417 -0
  32. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_102/main.py +280 -0
  33. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_102/original.py +356 -0
  34. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_102/rewrite.txt +271 -0
  35. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_103/edit.diff +270 -0
  36. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_103/main.py +266 -0
  37. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_103/original.py +265 -0
  38. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_103/search_replace.txt +32 -0
  39. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_104/edit.diff +295 -0
  40. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_104/main.py +266 -0
  41. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_104/original.py +252 -0
  42. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_104/rewrite.txt +257 -0
  43. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_105/edit.diff +419 -0
  44. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_105/main.py +405 -0
  45. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_105/original.py +402 -0
  46. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_105/rewrite.txt +396 -0
  47. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_106/edit.diff +400 -0
  48. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_106/main.py +376 -0
  49. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_106/original.py +356 -0
  50. examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_106/search_replace.txt +181 -0
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/best/edit.diff ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,291 +1,291 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+ from itertools import product
7
+
8
+
9
+ class CirclePacker:
10
+ """
11
+ A class to construct circle packings using a multi-stage optimization process:
12
+ 1. Initial 5x5 grid placement.
13
+ 2. A refined interstitial grid search for the 26th circle.
14
+ 3. A localized Simulated Annealing (SA) refinement of the resulting cluster.
15
+ """
16
+ def __init__(self, num_circles=26):
17
+ """Initializes the packer for 26 circles."""
18
+ if num_circles != 26:
19
+ raise ValueError("This CirclePacker is specialized for exactly 26 circles.")
20
+ self.n = num_circles
21
+ self.centers = np.zeros((self.n, 2))
22
+ self.radii = np.zeros(self.n)
23
+
24
+ @staticmethod
25
+ def _compute_max_radii_static(centers: np.ndarray) -> np.ndarray:
26
+ """
27
+ Computes maximum radii using an iterative method with an adaptive growth
28
+ factor and tolerance with exponential decay, adopted from the highest-scoring
29
+ prior implementations for superior performance.
30
+
31
+ Args:
32
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
33
+
34
+ Returns:
35
+ np.array of shape (n) with the final radius of each circle.
36
+ """
37
+ n = centers.shape[0]
38
+ radii = np.zeros(n)
39
+
40
+ # Parameters from the high-scoring prior program (score 2.5406)
41
+ growth_factor_start = 1.005
42
+ growth_factor_end = 1.002
43
+ outer_iterations = 400
44
+ tolerance_start = 1e-7
45
+ tolerance_end = 1e-11
46
+ # Increased inner iterations for more robust constraint satisfaction
47
+ inner_iterations = 20
48
+
49
+ # Initialize radii based on boundary distance
50
+ for i in range(n):
51
+ x, y = centers[i]
52
+ radii[i] = min(x, 1 - x, y, 1 - y)
53
+
54
+ # Iteratively grow and resolve constraints
55
+ for k in range(outer_iterations):
56
+ progress = k / (outer_iterations - 1 + 1e-9)
57
+ # Exponential interpolation for smooth parameter transition
58
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**progress
59
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**progress
60
+
61
+ radii *= current_growth_factor
62
+
63
+ for _ in range(inner_iterations):
64
+ constraints_changed = False
65
+ # Boundary constraints
66
+ for i in range(n):
67
+ x, y = centers[i]
68
+ boundary_limit = min(x, 1 - x, y, 1 - y)
69
+ if radii[i] > boundary_limit + current_tolerance:
70
+ radii[i] = boundary_limit
71
+ constraints_changed = True
72
+
73
+ # Overlap constraints
74
+ for i in range(n):
75
+ for j in range(i + 1, n):
76
+ dist = np.linalg.norm(centers[i] - centers[j])
77
+ if radii[i] + radii[j] > dist + current_tolerance:
78
+ total_radius = radii[i] + radii[j]
79
+ if total_radius > tolerance_end: # Use tolerance_end for consistency
80
+ scale = dist / total_radius
81
+ radii[i] *= scale
82
+ radii[j] *= scale
83
+ constraints_changed = True
84
+ if not constraints_changed:
85
+ break
86
+ return radii
87
+
88
+ def _initial_grid_placement(self) -> np.ndarray:
89
+ """Places the first 25 circles in a perfect 5x5 grid."""
90
+ coords = np.linspace(0.1, 0.9, 5)
91
+ return np.array(list(product(coords, coords)))
92
+
93
+ def _grid_search_for_26th_circle(self, base_centers: np.ndarray) -> tuple[np.ndarray, float]:
94
+ """
95
+ Performs a multi-resolution grid search for the 26th circle, identifying
96
+ promising regions in a coarse pass and then refining the search in a fine pass.
97
+ """
98
+ best_sum_radii = -1.0
99
+ best_centers_config = None
100
+
101
+ # --- Phase 1: Coarse Grid Search ---
102
+ - # Expanded initial range and moderate perturbations
103
+ - coarse_interstitial_coords = np.linspace(0.1, 0.9, 8) # e.g., 0.1 to 0.9 in 8 steps
104
+ - coarse_delta = 0.05
105
+ + # Search around core interstitial points, focused on the central region.
106
+ + coarse_interstitial_coords = np.linspace(0.2, 0.8, 4) # 4x4 grid of base points
107
+ + coarse_delta = 0.025
108
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta]) # 3 offsets
109
+
110
+ coarse_candidate_points_and_sums = []
111
+
112
+ for base_x, base_y in product(coarse_interstitial_coords, coarse_interstitial_coords):
113
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets):
114
+ candidate_pos = np.array([base_x + offset_x, base_y + offset_y])
115
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
116
+
117
+ trial_centers = np.vstack([base_centers, clipped_candidate_pos])
118
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
119
+ current_sum_radii = np.sum(trial_radii)
120
+
121
+ coarse_candidate_points_and_sums.append((current_sum_radii, clipped_candidate_pos))
122
+
123
+ if current_sum_radii > best_sum_radii:
124
+ best_sum_radii = current_sum_radii
125
+ best_centers_config = trial_centers
126
+
127
+ # --- Phase 2: Fine Grid Search around Top N Coarse Candidates ---
128
+ N_TOP_CANDIDATES = 5
129
+ # Sort by sum_radii in descending order and get the top N positions
130
+ coarse_candidate_points_and_sums.sort(key=lambda x: x[0], reverse=True)
131
+ top_coarse_positions = [item[1] for item in coarse_candidate_points_and_sums[:N_TOP_CANDIDATES]]
132
+
133
+ fine_delta = 0.01
134
+ fine_perturbation_offsets = np.linspace(-fine_delta, fine_delta, 5) # 5 finer offsets
135
+
136
+ for top_pos in top_coarse_positions:
137
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets):
138
+ candidate_pos = np.array([top_pos[0] + offset_x, top_pos[1] + offset_y])
139
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
140
+
141
+ trial_centers = np.vstack([base_centers, clipped_candidate_pos])
142
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
143
+ current_sum_radii = np.sum(trial_radii)
144
+
145
+ if current_sum_radii > best_sum_radii:
146
+ best_sum_radii = current_sum_radii
147
+ best_centers_config = trial_centers
148
+
149
+ if best_centers_config is None:
150
+ # Fallback if no valid positions yielded positive sum_radii (should not happen with good initial base_centers)
151
+ best_centers_config = np.vstack([base_centers, [[0.5, 0.5]]])
152
+ best_sum_radii = np.sum(CirclePacker._compute_max_radii_static(best_centers_config))
153
+
154
+ return best_centers_config, best_sum_radii
155
+
156
+ def _local_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
157
+ """
158
+ Applies a localized SA search to fine-tune the positions of the 26th circle and its 4 nearest neighbors.
159
+ """
160
+ # Tuned SA parameters for local cluster refinement
161
+ sa_iterations = 300
162
+ sa_initial_temp = 0.0002
163
+ sa_cooling_rate = 0.99
164
+ sa_initial_step_size = 0.005
165
+
166
+ current_centers = np.copy(initial_centers)
167
+ current_sum_radii = initial_sum_radii
168
+
169
+ best_centers = np.copy(current_centers)
170
+ best_sum_radii = current_sum_radii
171
+
172
+ temp = sa_initial_temp
173
+ step_size = sa_initial_step_size
174
+
175
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
176
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
177
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
178
+ cluster_indices = np.append(closest_neighbor_indices, 25)
179
+
180
+ for _ in range(sa_iterations):
181
+ idx_to_move = np.random.choice(cluster_indices)
182
+
183
+ trial_centers = np.copy(current_centers)
184
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
185
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
186
+
187
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
188
+ trial_sum_radii = np.sum(trial_radii)
189
+
190
+ delta_energy = trial_sum_radii - current_sum_radii
191
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
192
+ current_centers = trial_centers
193
+ current_sum_radii = trial_sum_radii
194
+
195
+ if current_sum_radii > best_sum_radii:
196
+ best_sum_radii = current_sum_radii
197
+ best_centers = np.copy(current_centers)
198
+
199
+ temp *= sa_cooling_rate
200
+ step_size = max(step_size * sa_cooling_rate, 1e-7)
201
+
202
+ return best_centers, best_sum_radii
203
+
204
+ def _global_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
205
+ """
206
+ Applies a global, low-temperature SA to "jiggle" all circles into a better global optimum.
207
+ This version prioritizes perturbing "stressed" circles (those with smaller radii).
208
+ """
209
+ # SA parameters for a final, gentle, global refinement
210
+ sa_iterations = 2000 # Increased iterations for more thorough search
211
+ sa_initial_temp = 1e-5
212
+ sa_cooling_rate = 0.995
213
+ sa_initial_step_size = 0.004
214
+
215
+ current_centers = np.copy(initial_centers)
216
+ current_radii = CirclePacker._compute_max_radii_static(current_centers) # Calculate initial radii for stress
217
+ current_sum_radii = initial_sum_radii
218
+
219
+ best_centers = np.copy(current_centers)
220
+ best_sum_radii = current_sum_radii
221
+
222
+ temp = sa_initial_temp
223
+ step_size = sa_initial_step_size
224
+
225
+ for _ in range(sa_iterations):
226
+ # Perturb a random circle from the entire set, prioritizing "stressed" circles (smaller radii)
227
+ # This implements Recommendation 4.
228
+ inverse_radii = 1.0 / (current_radii + 1e-9) # Add small epsilon to avoid division by zero
229
+ selection_probs = inverse_radii / np.sum(inverse_radii)
230
+ idx_to_move = np.random.choice(self.n, p=selection_probs)
231
+
232
+ trial_centers = np.copy(current_centers)
233
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
234
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
235
+
236
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
237
+ trial_sum_radii = np.sum(trial_radii)
238
+
239
+ delta_energy = trial_sum_radii - current_sum_radii
240
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
241
+ current_centers = trial_centers
242
+ current_sum_radii = trial_sum_radii
243
+ current_radii = trial_radii # Update current_radii after successful move
244
+
245
+ if current_sum_radii > best_sum_radii:
246
+ best_sum_radii = current_sum_radii
247
+ best_centers = np.copy(current_centers)
248
+
249
+ temp *= sa_cooling_rate
250
+ step_size = max(step_size * sa_cooling_rate, 5e-8)
251
+
252
+ return best_centers, best_sum_radii
253
+
254
+ def construct_packing(self):
255
+ """
256
+ Orchestrates the multi-stage packing process:
257
+ 1. Grid search for 26th circle.
258
+ 2. Local SA refinement of the resulting cluster.
259
+ 3. Global SA refinement of the entire packing.
260
+ """
261
+ base_centers = self._initial_grid_placement()
262
+
263
+ # Stage 1: Exhaustive search for a strong starting point
264
+ centers1, sum_radii1 = self._grid_search_for_26th_circle(base_centers)
265
+
266
+ # Stage 2: Local refinement on the cluster to fine-tune
267
+ centers2, sum_radii2 = self._local_refinement_sa(centers1, sum_radii1)
268
+
269
+ # Stage 3: Global "gentle jiggle" refinement on all circles
270
+ centers3, _ = self._global_refinement_sa(centers2, sum_radii2)
271
+
272
+ self.centers = centers3
273
+ # Final radius calculation for maximum precision on the best-found centers
274
+ self.radii = CirclePacker._compute_max_radii_static(self.centers)
275
+
276
+ return self.centers, self.radii
277
+
278
+
279
+ def construct_packing():
280
+ """
281
+ Constructs an arrangement of 26 circles by leveraging a multi-stage
282
+ optimization strategy managed by the CirclePacker class. This involves
283
+ a refined grid search followed by Simulated Annealing for local refinement.
284
+ """
285
+ packer = CirclePacker(num_circles=26)
286
+ centers, radii = packer.construct_packing()
287
+ return centers, radii
288
+ # EVOLVE-BLOCK-END
289
+
290
+
291
+ # This part remains fixed (not evolved)
292
+ def run_packing():
293
+ """Run the circle packing constructor for n=26"""
294
+ centers, radii = construct_packing()
295
+ # Calculate the sum of radii
296
+ sum_radii = np.sum(radii)
297
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/best/main.py ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings using a multi-stage optimization process:
9
+ 1. Initial 5x5 grid placement.
10
+ 2. A refined interstitial grid search for the 26th circle.
11
+ 3. A localized Simulated Annealing (SA) refinement of the resulting cluster.
12
+ """
13
+ def __init__(self, num_circles=26):
14
+ """Initializes the packer for 26 circles."""
15
+ if num_circles != 26:
16
+ raise ValueError("This CirclePacker is specialized for exactly 26 circles.")
17
+ self.n = num_circles
18
+ self.centers = np.zeros((self.n, 2))
19
+ self.radii = np.zeros(self.n)
20
+
21
+ @staticmethod
22
+ def _compute_max_radii_static(centers: np.ndarray) -> np.ndarray:
23
+ """
24
+ Computes maximum radii using an iterative method with an adaptive growth
25
+ factor and tolerance with exponential decay, adopted from the highest-scoring
26
+ prior implementations for superior performance.
27
+
28
+ Args:
29
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
30
+
31
+ Returns:
32
+ np.array of shape (n) with the final radius of each circle.
33
+ """
34
+ n = centers.shape[0]
35
+ radii = np.zeros(n)
36
+
37
+ # Parameters from the high-scoring prior program (score 2.5406)
38
+ growth_factor_start = 1.005
39
+ growth_factor_end = 1.002
40
+ outer_iterations = 400
41
+ tolerance_start = 1e-7
42
+ tolerance_end = 1e-11
43
+ # Increased inner iterations for more robust constraint satisfaction
44
+ inner_iterations = 20
45
+
46
+ # Initialize radii based on boundary distance
47
+ for i in range(n):
48
+ x, y = centers[i]
49
+ radii[i] = min(x, 1 - x, y, 1 - y)
50
+
51
+ # Iteratively grow and resolve constraints
52
+ for k in range(outer_iterations):
53
+ progress = k / (outer_iterations - 1 + 1e-9)
54
+ # Exponential interpolation for smooth parameter transition
55
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**progress
56
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**progress
57
+
58
+ radii *= current_growth_factor
59
+
60
+ for _ in range(inner_iterations):
61
+ constraints_changed = False
62
+ # Boundary constraints
63
+ for i in range(n):
64
+ x, y = centers[i]
65
+ boundary_limit = min(x, 1 - x, y, 1 - y)
66
+ if radii[i] > boundary_limit + current_tolerance:
67
+ radii[i] = boundary_limit
68
+ constraints_changed = True
69
+
70
+ # Overlap constraints
71
+ for i in range(n):
72
+ for j in range(i + 1, n):
73
+ dist = np.linalg.norm(centers[i] - centers[j])
74
+ if radii[i] + radii[j] > dist + current_tolerance:
75
+ total_radius = radii[i] + radii[j]
76
+ if total_radius > tolerance_end: # Use tolerance_end for consistency
77
+ scale = dist / total_radius
78
+ radii[i] *= scale
79
+ radii[j] *= scale
80
+ constraints_changed = True
81
+ if not constraints_changed:
82
+ break
83
+ return radii
84
+
85
+ def _initial_grid_placement(self) -> np.ndarray:
86
+ """Places the first 25 circles in a perfect 5x5 grid."""
87
+ coords = np.linspace(0.1, 0.9, 5)
88
+ return np.array(list(product(coords, coords)))
89
+
90
+ def _grid_search_for_26th_circle(self, base_centers: np.ndarray) -> tuple[np.ndarray, float]:
91
+ """
92
+ Performs a multi-resolution grid search for the 26th circle, identifying
93
+ promising regions in a coarse pass and then refining the search in a fine pass.
94
+ """
95
+ best_sum_radii = -1.0
96
+ best_centers_config = None
97
+
98
+ # --- Phase 1: Coarse Grid Search ---
99
+ # Search around core interstitial points, focused on the central region.
100
+ coarse_interstitial_coords = np.linspace(0.2, 0.8, 4) # 4x4 grid of base points
101
+ coarse_delta = 0.025
102
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta]) # 3 offsets
103
+
104
+ coarse_candidate_points_and_sums = []
105
+
106
+ for base_x, base_y in product(coarse_interstitial_coords, coarse_interstitial_coords):
107
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets):
108
+ candidate_pos = np.array([base_x + offset_x, base_y + offset_y])
109
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
110
+
111
+ trial_centers = np.vstack([base_centers, clipped_candidate_pos])
112
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
113
+ current_sum_radii = np.sum(trial_radii)
114
+
115
+ coarse_candidate_points_and_sums.append((current_sum_radii, clipped_candidate_pos))
116
+
117
+ if current_sum_radii > best_sum_radii:
118
+ best_sum_radii = current_sum_radii
119
+ best_centers_config = trial_centers
120
+
121
+ # --- Phase 2: Fine Grid Search around Top N Coarse Candidates ---
122
+ N_TOP_CANDIDATES = 5
123
+ # Sort by sum_radii in descending order and get the top N positions
124
+ coarse_candidate_points_and_sums.sort(key=lambda x: x[0], reverse=True)
125
+ top_coarse_positions = [item[1] for item in coarse_candidate_points_and_sums[:N_TOP_CANDIDATES]]
126
+
127
+ fine_delta = 0.01
128
+ fine_perturbation_offsets = np.linspace(-fine_delta, fine_delta, 5) # 5 finer offsets
129
+
130
+ for top_pos in top_coarse_positions:
131
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets):
132
+ candidate_pos = np.array([top_pos[0] + offset_x, top_pos[1] + offset_y])
133
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
134
+
135
+ trial_centers = np.vstack([base_centers, clipped_candidate_pos])
136
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
137
+ current_sum_radii = np.sum(trial_radii)
138
+
139
+ if current_sum_radii > best_sum_radii:
140
+ best_sum_radii = current_sum_radii
141
+ best_centers_config = trial_centers
142
+
143
+ if best_centers_config is None:
144
+ # Fallback if no valid positions yielded positive sum_radii (should not happen with good initial base_centers)
145
+ best_centers_config = np.vstack([base_centers, [[0.5, 0.5]]])
146
+ best_sum_radii = np.sum(CirclePacker._compute_max_radii_static(best_centers_config))
147
+
148
+ return best_centers_config, best_sum_radii
149
+
150
+ def _local_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
151
+ """
152
+ Applies a localized SA search to fine-tune the positions of the 26th circle and its 4 nearest neighbors.
153
+ """
154
+ # Tuned SA parameters for local cluster refinement
155
+ sa_iterations = 300
156
+ sa_initial_temp = 0.0002
157
+ sa_cooling_rate = 0.99
158
+ sa_initial_step_size = 0.005
159
+
160
+ current_centers = np.copy(initial_centers)
161
+ current_sum_radii = initial_sum_radii
162
+
163
+ best_centers = np.copy(current_centers)
164
+ best_sum_radii = current_sum_radii
165
+
166
+ temp = sa_initial_temp
167
+ step_size = sa_initial_step_size
168
+
169
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
170
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
171
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
172
+ cluster_indices = np.append(closest_neighbor_indices, 25)
173
+
174
+ for _ in range(sa_iterations):
175
+ idx_to_move = np.random.choice(cluster_indices)
176
+
177
+ trial_centers = np.copy(current_centers)
178
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
179
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
180
+
181
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
182
+ trial_sum_radii = np.sum(trial_radii)
183
+
184
+ delta_energy = trial_sum_radii - current_sum_radii
185
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
186
+ current_centers = trial_centers
187
+ current_sum_radii = trial_sum_radii
188
+
189
+ if current_sum_radii > best_sum_radii:
190
+ best_sum_radii = current_sum_radii
191
+ best_centers = np.copy(current_centers)
192
+
193
+ temp *= sa_cooling_rate
194
+ step_size = max(step_size * sa_cooling_rate, 1e-7)
195
+
196
+ return best_centers, best_sum_radii
197
+
198
+ def _global_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
199
+ """
200
+ Applies a global, low-temperature SA to "jiggle" all circles into a better global optimum.
201
+ This version prioritizes perturbing "stressed" circles (those with smaller radii).
202
+ """
203
+ # SA parameters for a final, gentle, global refinement
204
+ sa_iterations = 2000 # Increased iterations for more thorough search
205
+ sa_initial_temp = 1e-5
206
+ sa_cooling_rate = 0.995
207
+ sa_initial_step_size = 0.004
208
+
209
+ current_centers = np.copy(initial_centers)
210
+ current_radii = CirclePacker._compute_max_radii_static(current_centers) # Calculate initial radii for stress
211
+ current_sum_radii = initial_sum_radii
212
+
213
+ best_centers = np.copy(current_centers)
214
+ best_sum_radii = current_sum_radii
215
+
216
+ temp = sa_initial_temp
217
+ step_size = sa_initial_step_size
218
+
219
+ for _ in range(sa_iterations):
220
+ # Perturb a random circle from the entire set, prioritizing "stressed" circles (smaller radii)
221
+ # This implements Recommendation 4.
222
+ inverse_radii = 1.0 / (current_radii + 1e-9) # Add small epsilon to avoid division by zero
223
+ selection_probs = inverse_radii / np.sum(inverse_radii)
224
+ idx_to_move = np.random.choice(self.n, p=selection_probs)
225
+
226
+ trial_centers = np.copy(current_centers)
227
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
228
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
229
+
230
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
231
+ trial_sum_radii = np.sum(trial_radii)
232
+
233
+ delta_energy = trial_sum_radii - current_sum_radii
234
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
235
+ current_centers = trial_centers
236
+ current_sum_radii = trial_sum_radii
237
+ current_radii = trial_radii # Update current_radii after successful move
238
+
239
+ if current_sum_radii > best_sum_radii:
240
+ best_sum_radii = current_sum_radii
241
+ best_centers = np.copy(current_centers)
242
+
243
+ temp *= sa_cooling_rate
244
+ step_size = max(step_size * sa_cooling_rate, 5e-8)
245
+
246
+ return best_centers, best_sum_radii
247
+
248
+ def construct_packing(self):
249
+ """
250
+ Orchestrates the multi-stage packing process:
251
+ 1. Grid search for 26th circle.
252
+ 2. Local SA refinement of the resulting cluster.
253
+ 3. Global SA refinement of the entire packing.
254
+ """
255
+ base_centers = self._initial_grid_placement()
256
+
257
+ # Stage 1: Exhaustive search for a strong starting point
258
+ centers1, sum_radii1 = self._grid_search_for_26th_circle(base_centers)
259
+
260
+ # Stage 2: Local refinement on the cluster to fine-tune
261
+ centers2, sum_radii2 = self._local_refinement_sa(centers1, sum_radii1)
262
+
263
+ # Stage 3: Global "gentle jiggle" refinement on all circles
264
+ centers3, _ = self._global_refinement_sa(centers2, sum_radii2)
265
+
266
+ self.centers = centers3
267
+ # Final radius calculation for maximum precision on the best-found centers
268
+ self.radii = CirclePacker._compute_max_radii_static(self.centers)
269
+
270
+ return self.centers, self.radii
271
+
272
+
273
+ def construct_packing():
274
+ """
275
+ Constructs an arrangement of 26 circles by leveraging a multi-stage
276
+ optimization strategy managed by the CirclePacker class. This involves
277
+ a refined grid search followed by Simulated Annealing for local refinement.
278
+ """
279
+ packer = CirclePacker(num_circles=26)
280
+ centers, radii = packer.construct_packing()
281
+ return centers, radii
282
+ # EVOLVE-BLOCK-END
283
+
284
+
285
+ # This part remains fixed (not evolved)
286
+ def run_packing():
287
+ """Run the circle packing constructor for n=26"""
288
+ centers, radii = construct_packing()
289
+ # Calculate the sum of radii
290
+ sum_radii = np.sum(radii)
291
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/best/original.py ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings using a multi-stage optimization process:
9
+ 1. Initial 5x5 grid placement.
10
+ 2. A refined interstitial grid search for the 26th circle.
11
+ 3. A localized Simulated Annealing (SA) refinement of the resulting cluster.
12
+ """
13
+ def __init__(self, num_circles=26):
14
+ """Initializes the packer for 26 circles."""
15
+ if num_circles != 26:
16
+ raise ValueError("This CirclePacker is specialized for exactly 26 circles.")
17
+ self.n = num_circles
18
+ self.centers = np.zeros((self.n, 2))
19
+ self.radii = np.zeros(self.n)
20
+
21
+ @staticmethod
22
+ def _compute_max_radii_static(centers: np.ndarray) -> np.ndarray:
23
+ """
24
+ Computes maximum radii using an iterative method with an adaptive growth
25
+ factor and tolerance with exponential decay, adopted from the highest-scoring
26
+ prior implementations for superior performance.
27
+
28
+ Args:
29
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
30
+
31
+ Returns:
32
+ np.array of shape (n) with the final radius of each circle.
33
+ """
34
+ n = centers.shape[0]
35
+ radii = np.zeros(n)
36
+
37
+ # Parameters from the high-scoring prior program (score 2.5406)
38
+ growth_factor_start = 1.005
39
+ growth_factor_end = 1.002
40
+ outer_iterations = 400
41
+ tolerance_start = 1e-7
42
+ tolerance_end = 1e-11
43
+ # Increased inner iterations for more robust constraint satisfaction
44
+ inner_iterations = 20
45
+
46
+ # Initialize radii based on boundary distance
47
+ for i in range(n):
48
+ x, y = centers[i]
49
+ radii[i] = min(x, 1 - x, y, 1 - y)
50
+
51
+ # Iteratively grow and resolve constraints
52
+ for k in range(outer_iterations):
53
+ progress = k / (outer_iterations - 1 + 1e-9)
54
+ # Exponential interpolation for smooth parameter transition
55
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**progress
56
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**progress
57
+
58
+ radii *= current_growth_factor
59
+
60
+ for _ in range(inner_iterations):
61
+ constraints_changed = False
62
+ # Boundary constraints
63
+ for i in range(n):
64
+ x, y = centers[i]
65
+ boundary_limit = min(x, 1 - x, y, 1 - y)
66
+ if radii[i] > boundary_limit + current_tolerance:
67
+ radii[i] = boundary_limit
68
+ constraints_changed = True
69
+
70
+ # Overlap constraints
71
+ for i in range(n):
72
+ for j in range(i + 1, n):
73
+ dist = np.linalg.norm(centers[i] - centers[j])
74
+ if radii[i] + radii[j] > dist + current_tolerance:
75
+ total_radius = radii[i] + radii[j]
76
+ if total_radius > tolerance_end: # Use tolerance_end for consistency
77
+ scale = dist / total_radius
78
+ radii[i] *= scale
79
+ radii[j] *= scale
80
+ constraints_changed = True
81
+ if not constraints_changed:
82
+ break
83
+ return radii
84
+
85
+ def _initial_grid_placement(self) -> np.ndarray:
86
+ """Places the first 25 circles in a perfect 5x5 grid."""
87
+ coords = np.linspace(0.1, 0.9, 5)
88
+ return np.array(list(product(coords, coords)))
89
+
90
+ def _grid_search_for_26th_circle(self, base_centers: np.ndarray) -> tuple[np.ndarray, float]:
91
+ """
92
+ Performs a multi-resolution grid search for the 26th circle, identifying
93
+ promising regions in a coarse pass and then refining the search in a fine pass.
94
+ """
95
+ best_sum_radii = -1.0
96
+ best_centers_config = None
97
+
98
+ # --- Phase 1: Coarse Grid Search ---
99
+ # Expanded initial range and moderate perturbations
100
+ coarse_interstitial_coords = np.linspace(0.1, 0.9, 8) # e.g., 0.1 to 0.9 in 8 steps
101
+ coarse_delta = 0.05
102
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta]) # 3 offsets
103
+
104
+ coarse_candidate_points_and_sums = []
105
+
106
+ for base_x, base_y in product(coarse_interstitial_coords, coarse_interstitial_coords):
107
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets):
108
+ candidate_pos = np.array([base_x + offset_x, base_y + offset_y])
109
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
110
+
111
+ trial_centers = np.vstack([base_centers, clipped_candidate_pos])
112
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
113
+ current_sum_radii = np.sum(trial_radii)
114
+
115
+ coarse_candidate_points_and_sums.append((current_sum_radii, clipped_candidate_pos))
116
+
117
+ if current_sum_radii > best_sum_radii:
118
+ best_sum_radii = current_sum_radii
119
+ best_centers_config = trial_centers
120
+
121
+ # --- Phase 2: Fine Grid Search around Top N Coarse Candidates ---
122
+ N_TOP_CANDIDATES = 5
123
+ # Sort by sum_radii in descending order and get the top N positions
124
+ coarse_candidate_points_and_sums.sort(key=lambda x: x[0], reverse=True)
125
+ top_coarse_positions = [item[1] for item in coarse_candidate_points_and_sums[:N_TOP_CANDIDATES]]
126
+
127
+ fine_delta = 0.01
128
+ fine_perturbation_offsets = np.linspace(-fine_delta, fine_delta, 5) # 5 finer offsets
129
+
130
+ for top_pos in top_coarse_positions:
131
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets):
132
+ candidate_pos = np.array([top_pos[0] + offset_x, top_pos[1] + offset_y])
133
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
134
+
135
+ trial_centers = np.vstack([base_centers, clipped_candidate_pos])
136
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
137
+ current_sum_radii = np.sum(trial_radii)
138
+
139
+ if current_sum_radii > best_sum_radii:
140
+ best_sum_radii = current_sum_radii
141
+ best_centers_config = trial_centers
142
+
143
+ if best_centers_config is None:
144
+ # Fallback if no valid positions yielded positive sum_radii (should not happen with good initial base_centers)
145
+ best_centers_config = np.vstack([base_centers, [[0.5, 0.5]]])
146
+ best_sum_radii = np.sum(CirclePacker._compute_max_radii_static(best_centers_config))
147
+
148
+ return best_centers_config, best_sum_radii
149
+
150
+ def _local_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
151
+ """
152
+ Applies a localized SA search to fine-tune the positions of the 26th circle and its 4 nearest neighbors.
153
+ """
154
+ # Tuned SA parameters for local cluster refinement
155
+ sa_iterations = 300
156
+ sa_initial_temp = 0.0002
157
+ sa_cooling_rate = 0.99
158
+ sa_initial_step_size = 0.005
159
+
160
+ current_centers = np.copy(initial_centers)
161
+ current_sum_radii = initial_sum_radii
162
+
163
+ best_centers = np.copy(current_centers)
164
+ best_sum_radii = current_sum_radii
165
+
166
+ temp = sa_initial_temp
167
+ step_size = sa_initial_step_size
168
+
169
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
170
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
171
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
172
+ cluster_indices = np.append(closest_neighbor_indices, 25)
173
+
174
+ for _ in range(sa_iterations):
175
+ idx_to_move = np.random.choice(cluster_indices)
176
+
177
+ trial_centers = np.copy(current_centers)
178
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
179
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
180
+
181
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
182
+ trial_sum_radii = np.sum(trial_radii)
183
+
184
+ delta_energy = trial_sum_radii - current_sum_radii
185
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
186
+ current_centers = trial_centers
187
+ current_sum_radii = trial_sum_radii
188
+
189
+ if current_sum_radii > best_sum_radii:
190
+ best_sum_radii = current_sum_radii
191
+ best_centers = np.copy(current_centers)
192
+
193
+ temp *= sa_cooling_rate
194
+ step_size = max(step_size * sa_cooling_rate, 1e-7)
195
+
196
+ return best_centers, best_sum_radii
197
+
198
+ def _global_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
199
+ """
200
+ Applies a global, low-temperature SA to "jiggle" all circles into a better global optimum.
201
+ This version prioritizes perturbing "stressed" circles (those with smaller radii).
202
+ """
203
+ # SA parameters for a final, gentle, global refinement
204
+ sa_iterations = 2000 # Increased iterations for more thorough search
205
+ sa_initial_temp = 1e-5
206
+ sa_cooling_rate = 0.995
207
+ sa_initial_step_size = 0.004
208
+
209
+ current_centers = np.copy(initial_centers)
210
+ current_radii = CirclePacker._compute_max_radii_static(current_centers) # Calculate initial radii for stress
211
+ current_sum_radii = initial_sum_radii
212
+
213
+ best_centers = np.copy(current_centers)
214
+ best_sum_radii = current_sum_radii
215
+
216
+ temp = sa_initial_temp
217
+ step_size = sa_initial_step_size
218
+
219
+ for _ in range(sa_iterations):
220
+ # Perturb a random circle from the entire set, prioritizing "stressed" circles (smaller radii)
221
+ # This implements Recommendation 4.
222
+ inverse_radii = 1.0 / (current_radii + 1e-9) # Add small epsilon to avoid division by zero
223
+ selection_probs = inverse_radii / np.sum(inverse_radii)
224
+ idx_to_move = np.random.choice(self.n, p=selection_probs)
225
+
226
+ trial_centers = np.copy(current_centers)
227
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
228
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
229
+
230
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
231
+ trial_sum_radii = np.sum(trial_radii)
232
+
233
+ delta_energy = trial_sum_radii - current_sum_radii
234
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
235
+ current_centers = trial_centers
236
+ current_sum_radii = trial_sum_radii
237
+ current_radii = trial_radii # Update current_radii after successful move
238
+
239
+ if current_sum_radii > best_sum_radii:
240
+ best_sum_radii = current_sum_radii
241
+ best_centers = np.copy(current_centers)
242
+
243
+ temp *= sa_cooling_rate
244
+ step_size = max(step_size * sa_cooling_rate, 5e-8)
245
+
246
+ return best_centers, best_sum_radii
247
+
248
+ def construct_packing(self):
249
+ """
250
+ Orchestrates the multi-stage packing process:
251
+ 1. Grid search for 26th circle.
252
+ 2. Local SA refinement of the resulting cluster.
253
+ 3. Global SA refinement of the entire packing.
254
+ """
255
+ base_centers = self._initial_grid_placement()
256
+
257
+ # Stage 1: Exhaustive search for a strong starting point
258
+ centers1, sum_radii1 = self._grid_search_for_26th_circle(base_centers)
259
+
260
+ # Stage 2: Local refinement on the cluster to fine-tune
261
+ centers2, sum_radii2 = self._local_refinement_sa(centers1, sum_radii1)
262
+
263
+ # Stage 3: Global "gentle jiggle" refinement on all circles
264
+ centers3, _ = self._global_refinement_sa(centers2, sum_radii2)
265
+
266
+ self.centers = centers3
267
+ # Final radius calculation for maximum precision on the best-found centers
268
+ self.radii = CirclePacker._compute_max_radii_static(self.centers)
269
+
270
+ return self.centers, self.radii
271
+
272
+
273
+ def construct_packing():
274
+ """
275
+ Constructs an arrangement of 26 circles by leveraging a multi-stage
276
+ optimization strategy managed by the CirclePacker class. This involves
277
+ a refined grid search followed by Simulated Annealing for local refinement.
278
+ """
279
+ packer = CirclePacker(num_circles=26)
280
+ centers, radii = packer.construct_packing()
281
+ return centers, radii
282
+ # EVOLVE-BLOCK-END
283
+
284
+
285
+ # This part remains fixed (not evolved)
286
+ def run_packing():
287
+ """Run the circle packing constructor for n=26"""
288
+ centers, radii = construct_packing()
289
+ # Calculate the sum of radii
290
+ sum_radii = np.sum(radii)
291
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/best/search_replace.txt ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <refine_26th_search>
2
+ A refinement of the 26th circle's initial placement strategy. This edit reverts the coarse search grid for the 26th circle to a more focused `4x4` pattern within the central `[0.2, 0.8]` region, instead of the broader `8x8` search over `[0.1, 0.9]`. The perturbation delta is also reduced accordingly. This change is based on prior successful implementations, where a more targeted initial search for the interstitial circle often leads to a better starting point for subsequent SA stages without over-exploring less promising areas near boundaries. The aim is to increase the efficiency of finding a high-quality initial placement, which can positively impact the final sum of radii.
3
+ </NAME>
4
+
5
+ <DIFF>
6
+ <<<<<<< SEARCH
7
+ # --- Phase 1: Coarse Grid Search ---
8
+ # Expanded initial range and moderate perturbations
9
+ coarse_interstitial_coords = np.linspace(0.1, 0.9, 8) # e.g., 0.1 to 0.9 in 8 steps
10
+ coarse_delta = 0.05
11
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta]) # 3 offsets
12
+ =======
13
+ # --- Phase 1: Coarse Grid Search ---
14
+ # Search around core interstitial points, focused on the central region.
15
+ coarse_interstitial_coords = np.linspace(0.2, 0.8, 4) # 4x4 grid of base points
16
+ coarse_delta = 0.025
17
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta]) # 3 offsets
18
+ >>>>>>> REPLACE
19
+ </DIFF>
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory/EVAL_AGENTS.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVAL_AGENTS.md - Auxiliary Evaluation Metrics
2
+
3
+ This file serves as a memory for the evaluation agent, documenting insights, successful auxiliary metrics, and patterns observed across generations during the code evolution process.
4
+
5
+ ## Generation 19 Evaluation Summary
6
+
7
+ **Primary Score (Generation 19):** 1.9201
8
+
9
+ ### Auxiliary Metrics Introduced (with Python implementation `aux_metrics.py`):
10
+
11
+ 1. **Total Overlap Severity (`total_overlap_severity`)**
12
+ * **Purpose**: Quantifies the extent of overlap between circles. A value greater than 0 indicates overlapping circles. This provides a gradient of 'badness' beyond a simple boolean validation failure.
13
+ * **Calculation**: Sum of `(radii[i] + radii[j] - dist)` for all overlapping pairs `(i, j)`. Only positive overlap values contribute.
14
+ * **Generation 19 Result**: `0.000000`
15
+ * **Insight**: The current solution successfully avoids circle overlaps, meeting a critical validity constraint. This metric confirms that the solution is well within acceptable bounds for overlap.
16
+
17
+ 2. **Total Out-of-Bounds Severity (`total_out_of_bounds_severity`)**
18
+ * **Purpose**: Quantifies how far circles extend beyond the [0,1]x[0,1] unit square boundaries. A value greater than 0 indicates circles extending outside the unit square.
19
+ * **Calculation**: Sum of `max(0, r - x)`, `max(0, x + r - 1)`, `max(0, r - y)`, `max(0, y + r - 1)` for all circles.
20
+ * **Generation 19 Result**: `0.0`
21
+ * **Insight**: The current solution keeps all circles strictly within the unit square, fulfilling another key validity constraint. This confirms robust handling of boundary conditions.
22
+
23
+ 3. **Average Distance to Unit Center (`average_distance_to_unit_center`)**
24
+ * **Purpose**: Measures the average Euclidean distance of each circle's center from the center of the unit square (0.5, 0.5). A lower value indicates a more centralized and potentially compact packing.
25
+ * **Calculation**: `np.mean(np.linalg.norm(centers - [0.5, 0.5], axis=1))`.
26
+ * **Generation 19 Result**: `0.365894`
27
+ * **Insight**: This value suggests that circles are not highly clustered towards the absolute center of the unit square. For maximizing total radii, a more centralized packing might be beneficial, especially if it allows for larger circles. This metric identifies an area for potential optimization, suggesting that strategies for tighter central packing could lead to higher scores.
28
+
29
+ 4. **Radii Standard Deviation (`radii_std_dev`)**
30
+ * **Purpose**: Measures the dispersion (variety) of the circle radii. A low standard deviation indicates similar-sized circles, while a high standard deviation suggests a mix of large and small circles.
31
+ * **Calculation**: `np.std(radii)`.
32
+ * **Generation 19 Result**: `0.025924`
33
+ * **Insight**: A relatively low standard deviation indicates that the current solution favors circles of somewhat uniform size. In some packing problems, a more diverse distribution of radii (e.g., a few large circles filling primary voids, surrounded by many smaller circles) can lead to higher total area. This metric suggests an opportunity to explore solutions with greater variation in circle sizes to potentially discover better packing configurations.
34
+
35
+ ### Evaluation Stage Assessment:
36
+ * **Stage**: Optimization / Convergence
37
+ * **Rationale**: The solution is valid (near-zero overlap and out-of-bounds severity), indicating that basic correctness has been largely achieved. The current challenge is to further increase the primary score (sum of radii), suggesting a focus on refining the packing strategy.
38
+
39
+ ### Recommendations for Future Evolution:
40
+
41
+ * **Track and Analyze Trends:** Continuously monitor `average_distance_to_unit_center` and `radii_std_dev` over subsequent generations.
42
+ * If `primary_score` plateaus, analyze the trend of `radii_std_dev`. If it's consistently low, consider introducing a temporary reward or exploration pressure towards higher `radii_std_dev` to encourage more diverse circle sizes, potentially escaping local optima.
43
+ * Observe if `average_distance_to_unit_center` correlates positively or negatively with `primary_score` to understand the optimal spatial arrangement.
44
+ * **Dynamic Weighting:** These auxiliary metrics could be integrated into a multi-objective evaluation framework, with their weights adjusted based on the current evolution stage:
45
+ * **EXPLORATION:** Prioritize penalizing `total_overlap_severity` and `total_out_of_bounds_severity` to guide towards feasible solutions.
46
+ * **OPTIMIZATION/CONVERGENCE:** Focus on `primary_score`, but use `average_distance_to_unit_center` to fine-tune spatial arrangement. If stagnation occurs, use `radii_std_dev` as a lever for renewed exploration.
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory/aux_metrics.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import numpy as np
3
+ import os
4
+ from typing import Dict, Any
5
+
6
+ def calculate_auxiliary_metrics(results_dir: str) -> Dict[str, Any]:
7
+ """
8
+ Calculates auxiliary metrics for circle packing solutions.
9
+
10
+ Args:
11
+ results_dir: The directory where evaluation results (including extra.npz) are stored.
12
+
13
+ Returns:
14
+ A dictionary containing auxiliary metrics.
15
+ """
16
+
17
+ extra_file = os.path.join(results_dir, "extra.npz")
18
+ metrics = {}
19
+
20
+ if not os.path.exists(extra_file):
21
+ metrics["aux_metrics_error"] = "extra.npz not found."
22
+ return metrics
23
+
24
+ try:
25
+ with np.load(extra_file) as data:
26
+ centers = data["centers"]
27
+ radii = data["radii"]
28
+ reported_sum = data["reported_sum"]
29
+ except Exception as e:
30
+ metrics["aux_metrics_error"] = f"Error loading extra.npz: {e}"
31
+ return metrics
32
+
33
+ n_expected = 26
34
+
35
+ # 1. Overlap Severity and Out-of-Bounds Severity
36
+ total_overlap_severity = 0.0
37
+ total_out_of_bounds_severity = 0.0
38
+
39
+ if centers.shape[0] == n_expected and radii.shape[0] == n_expected: # Only calculate if shapes are correct
40
+ # Out-of-bounds severity
41
+ for i in range(n_expected):
42
+ x, y = centers[i]
43
+ r = radii[i]
44
+ total_out_of_bounds_severity += max(0, r - x) # left bound (x-r < 0)
45
+ total_out_of_bounds_severity += max(0, x + r - 1) # right bound (x+r > 1)
46
+ total_out_of_bounds_severity += max(0, r - y) # bottom bound (y-r < 0)
47
+ total_out_of_bounds_severity += max(0, y + r - 1) # top bound (y+r > 1)
48
+
49
+ # Overlap severity
50
+ for i in range(n_expected):
51
+ for j in range(i + 1, n_expected):
52
+ dist = np.linalg.norm(centers[i] - centers[j])
53
+ overlap = (radii[i] + radii[j]) - dist
54
+ total_overlap_severity += max(0, overlap) # Only add if overlap is positive
55
+
56
+ metrics["total_overlap_severity"] = total_overlap_severity
57
+ metrics["total_out_of_bounds_severity"] = total_out_of_bounds_severity
58
+
59
+ # 2. Average Distance to Center of Unit Square (0.5, 0.5)
60
+ center_point = np.array([0.5, 0.5])
61
+ distances_to_center = np.linalg.norm(centers - center_point, axis=1)
62
+ metrics["average_distance_to_unit_center"] = np.mean(distances_to_center)
63
+
64
+ # 3. Radii Standard Deviation
65
+ if len(radii) > 0:
66
+ metrics["radii_std_dev"] = np.std(radii)
67
+ else:
68
+ metrics["radii_std_dev"] = 0.0 # Or np.nan, depending on desired behavior for empty radii
69
+
70
+ return metrics
71
+
72
+ if __name__ == "__main__":
73
+ # Example usage (for testing purposes)
74
+ # This part won't be run by the agent during evaluation, but useful for local testing
75
+
76
+ # Create dummy extra.npz for testing
77
+ dummy_results_dir = "./temp_results_dir"
78
+ os.makedirs(dummy_results_dir, exist_ok=True)
79
+
80
+ dummy_centers = np.array([[0.2, 0.2], [0.8, 0.8], [0.5, 0.5], [0.1, 0.1], [0.9, 0.9]])
81
+ dummy_radii = np.array([0.1, 0.1, 0.2, 0.05, 0.05])
82
+ dummy_reported_sum = np.sum(dummy_radii)
83
+
84
+ # Ensure 26 circles for testing validation logic
85
+ n_fill = 26 - len(dummy_centers)
86
+ if n_fill > 0:
87
+ dummy_centers = np.vstack([dummy_centers, np.random.rand(n_fill, 2)])
88
+ dummy_radii = np.hstack([dummy_radii, np.random.rand(n_fill) * 0.01]) # Small random radii
89
+
90
+ np.savez(os.path.join(dummy_results_dir, "extra.npz"),
91
+ centers=dummy_centers, radii=dummy_radii, reported_sum=dummy_reported_sum)
92
+
93
+ aux_metrics = calculate_auxiliary_metrics(dummy_results_dir)
94
+ print("Auxiliary Metrics:", aux_metrics)
95
+
96
+ # Clean up dummy files
97
+ os.remove(os.path.join(dummy_results_dir, "extra.npz"))
98
+ os.rmdir(dummy_results_dir)
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory/run_aux_metrics.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import sys
3
+ import os
4
+
5
+ # Add the current directory to sys.path to import aux_metrics
6
+ sys.path.insert(0, os.path.dirname(__file__))
7
+ from aux_metrics import calculate_auxiliary_metrics
8
+
9
+ def main():
10
+ gen_path = sys.argv[1] # Path to the current generation directory
11
+ results_dir = os.path.join(gen_path, 'results')
12
+
13
+ try:
14
+ metrics = calculate_auxiliary_metrics(results_dir)
15
+
16
+ for key, value in metrics.items():
17
+ print(f"Auxiliary Metric: {key} = {value:.6f}")
18
+
19
+ except Exception as e:
20
+ print(f"An error occurred: {e}")
21
+
22
+ if __name__ == "__main__":
23
+ main()
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory/service_state.json ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "generation_history": [
3
+ {
4
+ "generation": 0,
5
+ "primary_score": 2.4,
6
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
7
+ "timestamp": 1770062820.8740644
8
+ },
9
+ {
10
+ "generation": 1,
11
+ "primary_score": 2.4051,
12
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
13
+ "timestamp": 1770062820.979731
14
+ },
15
+ {
16
+ "generation": 2,
17
+ "primary_score": 2.4101999999999997,
18
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
19
+ "timestamp": 1770062821.0850384
20
+ },
21
+ {
22
+ "generation": 3,
23
+ "primary_score": 2.4153000000000002,
24
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
25
+ "timestamp": 1770062821.1902685
26
+ },
27
+ {
28
+ "generation": 4,
29
+ "primary_score": 2.4204,
30
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
31
+ "timestamp": 1770062821.295889
32
+ },
33
+ {
34
+ "generation": 5,
35
+ "primary_score": 2.4255,
36
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
37
+ "timestamp": 1770062821.4016712
38
+ },
39
+ {
40
+ "generation": 6,
41
+ "primary_score": 2.4305999999999996,
42
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
43
+ "timestamp": 1770062821.5073073
44
+ },
45
+ {
46
+ "generation": 7,
47
+ "primary_score": 2.4357,
48
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
49
+ "timestamp": 1770062821.6131945
50
+ },
51
+ {
52
+ "generation": 8,
53
+ "primary_score": 2.4408,
54
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
55
+ "timestamp": 1770062821.7192042
56
+ },
57
+ {
58
+ "generation": 9,
59
+ "primary_score": 2.4459,
60
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
61
+ "timestamp": 1770062821.8252268
62
+ },
63
+ {
64
+ "generation": 10,
65
+ "primary_score": 2.4509999999999996,
66
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
67
+ "timestamp": 1770062912.9271977
68
+ },
69
+ {
70
+ "generation": 11,
71
+ "primary_score": 2.4561,
72
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
73
+ "timestamp": 1770062913.032786
74
+ },
75
+ {
76
+ "generation": 12,
77
+ "primary_score": 2.4612,
78
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
79
+ "timestamp": 1770062913.1386952
80
+ },
81
+ {
82
+ "generation": 13,
83
+ "primary_score": 2.4663,
84
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
85
+ "timestamp": 1770062913.2447174
86
+ },
87
+ {
88
+ "generation": 14,
89
+ "primary_score": 2.4713999999999996,
90
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
91
+ "timestamp": 1770062913.350542
92
+ },
93
+ {
94
+ "generation": 1,
95
+ "primary_score": 1.2957387034310313,
96
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_1/results",
97
+ "timestamp": 1770077545.7928894
98
+ },
99
+ {
100
+ "generation": 2,
101
+ "primary_score": 0.0,
102
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_2/results",
103
+ "timestamp": 1770077645.315356
104
+ },
105
+ {
106
+ "generation": 3,
107
+ "primary_score": 1.3779523687973616,
108
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_3/results",
109
+ "timestamp": 1770077714.4659176
110
+ },
111
+ {
112
+ "generation": 4,
113
+ "primary_score": 1.2957192365106578,
114
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_4/results",
115
+ "timestamp": 1770077769.6756663
116
+ },
117
+ {
118
+ "generation": 5,
119
+ "primary_score": 0.0,
120
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_5/results",
121
+ "timestamp": 1770077885.8956773
122
+ },
123
+ {
124
+ "generation": 6,
125
+ "primary_score": 1.613114191172248,
126
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_6/results",
127
+ "timestamp": 1770077993.3654912
128
+ },
129
+ {
130
+ "generation": 7,
131
+ "primary_score": 0.0,
132
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_7/results",
133
+ "timestamp": 1770078061.7032235
134
+ },
135
+ {
136
+ "generation": 8,
137
+ "primary_score": 1.4247905652887678,
138
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_8/results",
139
+ "timestamp": 1770078229.7272182
140
+ },
141
+ {
142
+ "generation": 9,
143
+ "primary_score": 1.6560915267313265,
144
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_9/results",
145
+ "timestamp": 1770078342.0215967
146
+ },
147
+ {
148
+ "generation": 10,
149
+ "primary_score": 1.8683988559993825,
150
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_10/results",
151
+ "timestamp": 1770078503.12913
152
+ },
153
+ {
154
+ "generation": 11,
155
+ "primary_score": 1.6664656324609348,
156
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_11/results",
157
+ "timestamp": 1770078548.1208487
158
+ },
159
+ {
160
+ "generation": 12,
161
+ "primary_score": 1.8683988559993825,
162
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_12/results",
163
+ "timestamp": 1770078572.1637895
164
+ },
165
+ {
166
+ "generation": 13,
167
+ "primary_score": 1.743544972870135,
168
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_13/results",
169
+ "timestamp": 1770078642.4562724
170
+ },
171
+ {
172
+ "generation": 14,
173
+ "primary_score": 1.973952713938345,
174
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_14/results",
175
+ "timestamp": 1770078688.8499267
176
+ },
177
+ {
178
+ "generation": 15,
179
+ "primary_score": 0.0,
180
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_15/results",
181
+ "timestamp": 1770078714.6741428
182
+ },
183
+ {
184
+ "generation": 16,
185
+ "primary_score": 1.743544972870135,
186
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_16/results",
187
+ "timestamp": 1770078795.7139282
188
+ },
189
+ {
190
+ "generation": 17,
191
+ "primary_score": 2.3333333333333326,
192
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_17/results",
193
+ "timestamp": 1770078884.1817498
194
+ },
195
+ {
196
+ "generation": 18,
197
+ "primary_score": 1.8726437139594643,
198
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_18/results",
199
+ "timestamp": 1770078984.2956784
200
+ },
201
+ {
202
+ "generation": 19,
203
+ "primary_score": 0.0,
204
+ "results_dir": "examples/circle_packing/results/results_circle_packing_integration_test_with_service_passive_20260203_000916/gen_19/results",
205
+ "timestamp": 1770079037.285556
206
+ }
207
+ ],
208
+ "last_agent_trigger_gen": 19,
209
+ "total_notifications": 34,
210
+ "total_agent_runs": 2,
211
+ "last_update": 1770080831.396193
212
+ }
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory_backup_20260202_200614/EVAL_AGENTS.md ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Evaluation Agent Memory
2
+
3
+ This document tracks insights, successful auxiliary metrics, and patterns observed across generations during the code evolution process.
4
+
5
+ ## Auxiliary Metrics Design
6
+
7
+ ### Metric 1: Radii Distribution Uniformity (Coefficient of Variation)
8
+ * **Description**: Measures the spread of radii relative to their mean. A lower value indicates more uniform circle sizes, while a higher value suggests a greater disparity (e.g., a few very large circles and many small ones). This metric helps to understand the balancing act between few large circles versus many smaller, more evenly sized circles to achieve a high total radius sum.
9
+ * **Implementation**: `np.std(radii) / np.mean(radii)`
10
+ * **Insight**: Solutions aiming for a high total radius might sometimes achieve it by making one or two circles very large, at the expense of others. This metric provides insight into whether the packing strategy prioritizes uniform sizing or allows for a wide range of sizes.
11
+
12
+ ### Metric 2: Average Minimum Distance to Boundary
13
+ * **Description**: Calculates the average of the minimum distances from each circle's edge to the closest boundary of the unit square. This serves as an indicator of the 'robustness' or 'breathing room' of the packing.
14
+ * **Implementation**: For each circle (x, y, r), calculate `min(x-r, 1-(x+r), y-r, 1-(y+r))` and then average these minimums.
15
+ * **Insight**: A higher average minimum distance might suggest a more stable packing that is less sensitive to slight perturbations or numerical inaccuracies. Solutions with circles "kissing" the boundaries might be more fragile.
16
+
17
+ ### Metric 3: Packing Area Efficiency
18
+ * **Description**: The sum of the areas of all packed circles, divided by the total area of the unit square (which is 1.0). This provides a measure of how much of the available area is physically covered by the circles.
19
+ * **Implementation**: `sum(math.pi * r^2 for r in radii) / 1.0`
20
+ * **Insight**: While the primary metric focuses on the sum of radii, this metric gives a direct sense of the spatial utilization. A high sum of radii doesn't always translate to the highest area coverage, especially if many small circles are present. This helps differentiate between solutions that achieve high radius sum through few large circles versus many small circles.
21
+
22
+ ---
23
+
24
+ ## Generation 100 Evaluation Summary
25
+
26
+ **Primary Score**: 2.5405
27
+
28
+ **Calculated Auxiliary Metrics**:
29
+ * `radii_cv`: 0.1151 (Indicates a relatively uniform distribution of radii, not heavily skewed towards very large or very small circles.)
30
+ * `avg_min_dist_to_boundary`: 0.0831 (Suggests circles generally maintain a small but positive distance from the square boundaries, indicating a valid and somewhat robust packing.)
31
+ * `packing_area_efficiency`: 0.7902 (Approximately 79% of the unit square's area is covered by the circles, which is a good efficiency for this problem.)
32
+
33
+ **Analysis & Recommendations**:
34
+
35
+ * **Observation**: Generation 100 has a `combined_score` (sum of radii) of 2.5405. The auxiliary metrics provide further insights into the *nature* of this packing.
36
+ * **Radii CV (0.1151)**: This low coefficient of variation suggests the solution found a way to pack circles with relatively similar sizes, avoiding extreme size differences. This could be beneficial for stability or aesthetic reasons, though not directly optimized by the primary metric.
37
+ * **Avg Min Dist to Boundary (0.0831)**: The positive average minimum distance indicates that circles are not right up against the boundaries. This suggests the solution is not "cheating" by placing circles infinitesimally close to the edge, making it potentially more robust.
38
+ * **Packing Area Efficiency (0.7902)**: This value shows a significant portion of the square's area is covered. Comparing this to the primary score could highlight whether increasing radius sum comes at the cost of area efficiency (e.g., if many tiny circles contribute little to area but some to radius sum).
39
+
40
+ **Next Steps**:
41
+ Continue monitoring these auxiliary metrics across generations. If the primary score plateaus, these metrics can help identify new directions for exploration:
42
+ * Could increasing `packing_area_efficiency` lead to higher primary scores, or are there trade-offs?
43
+ * Can we achieve higher `avg_min_dist_to_boundary` while maintaining the primary score, indicating more robust solutions?
44
+ * How does `radii_cv` correlate with `combined_score` over evolution? Does a more uniform distribution lead to better packing in the long run, or do optimal solutions require diverse radii?
45
+
46
+ ---
47
+ ### Generation 9 Evaluation
48
+
49
+ **Primary Score**: 0.4256
50
+
51
+ **Auxiliary Metrics**:
52
+ * **Code Lines**: 67
53
+ * *Definition*: Number of non-empty, non-comment lines in `main.py`.
54
+ * *Purpose*: A proxy for code complexity and conciseness. Lower values might indicate simpler, potentially more robust or efficient solutions, or conversely, less comprehensive solutions.
55
+ * *Value for Gen 9*: 67 lines. This suggests a relatively concise solution.
56
+
57
+ * **Max Overlap Magnitude**: 0.0
58
+ * *Definition*: The largest positive difference between the sum of radii of any two circles and the distance between their centers. Returns 0.0 if no overlap.
59
+ * *Purpose*: Provides a "nearness to validity" score for solutions that fail due to overlaps. A value close to 0 but > 0 means it's "almost valid".
60
+ * *Value for Gen 9*: 0.0. This indicates that the solution successfully avoids overlaps (as confirmed by the primary evaluator likely passing this check, leading to a non-zero primary score).
61
+
62
+ * **Radii Standard Deviation**: 0.0558
63
+ * *Definition*: The standard deviation of the radii of all circles.
64
+ * *Purpose*: Measures the diversity in circle sizes. A higher standard deviation indicates a wider range of circle sizes, which might be beneficial for packing efficiency in certain configurations or for exploring different packing strategies. A very low standard deviation might suggest uniform circle sizes.
65
+ * *Value for Gen 9*: 0.0558. This indicates some variation in radii, but not extremely diverse.
66
+
67
+ **Overall Insights & Recommendations for Gen 9**:
68
+ * The solution for Generation 9 achieves a primary score of 0.4256, and crucially, has no overlaps (`max_overlap_magnitude: 0.0`). This means it's a valid packing in terms of non-overlap.
69
+ * The code complexity (67 lines) is moderate, which is a good baseline.
70
+ * The `radii_std_dev` of 0.0558 suggests that the solution is not using circles of uniform size, indicating some exploration of different circle distributions.
71
+
72
+ **Next Steps / Recommendations for Evolution**:
73
+ * Since overlap is 0.0, the current evolution is finding valid packings. The focus should now be on **optimization**: increasing the `reported_sum_of_radii` (primary score).
74
+ * Encourage solutions that might lead to a higher `radii_std_dev` if it correlates with higher primary scores in future generations, as this could indicate more effective utilization of space by varying circle sizes more dramatically.
75
+ * Monitor `code_lines` to ensure that increasing performance doesn't lead to excessively complex or unmanageable code. If `code_lines` starts increasing significantly without a proportional increase in primary score, it might be a sign of over-complication or inefficient strategies.
76
+
77
+ ## Generation 19 Evaluation Summary
78
+
79
+ **Primary Metric Status:**
80
+ * The primary evaluation metric for the circle packing problem aims to **minimize the sum of radii** for 26 non-overlapping circles within a unit square. A lower score is better.
81
+ * Current Primary Score (Gen 19): `1.9201`
82
+
83
+ **Auxiliary Metrics Introduced:**
84
+
85
+ Based on the analysis of `evaluate_ori.py`, the primary metric provides a pass/fail for validity (no overlaps, no boundary violations) and then scores based on the sum of radii. To provide richer feedback, especially for solutions that fail validation, and to understand solution characteristics, the following auxiliary metrics were developed:
86
+
87
+ 1. **`overlap_score`**
88
+ * **Purpose**: Quantifies the total "depth" of overlap between all pairs of circles. For valid solutions, this should be 0. For invalid solutions, a smaller positive value is better, indicating less severe overlaps. This helps differentiate between severely overlapping solutions and slightly overlapping ones, providing a gradient for optimization.
89
+ * **Calculation**: For each pair of circles `(i, j)`, calculate `radii[i] + radii[j] - dist(centers[i], centers[j])`. Sum all positive values (where overlap occurs).
90
+ * **Gen 19 Result**: `0.0` (indicates no overlaps)
91
+
92
+ 2. **`boundary_violation_score`**
93
+ * **Purpose**: Quantifies how much circles extend beyond the unit square boundaries `[0, 1] x [0, 1]`. For valid solutions, this should be 0. For invalid solutions, a smaller positive value is better. This provides a gradient for solutions that are "almost" in bounds versus significantly out of bounds.
94
+ * **Calculation**: For each circle and each of its four sides, calculate `max(0, boundary_edge - limit)`. Sum these values for all violations.
95
+ * **Gen 19 Result**: `0.0` (indicates no boundary violations)
96
+
97
+ 3. **`radius_statistics` (mean, std_dev, min, max)**
98
+ * **Purpose**: Provides insights into the distribution and diversity of circle radii used in the packing solution.
99
+ * `mean_radius`: Average size of circles.
100
+ * `std_dev_radius`: Standard deviation of radii, indicating the spread of circle sizes. A higher standard deviation suggests a more diverse set of circle sizes, which might be beneficial for complex packing.
101
+ * `min_radius`, `max_radius`: Smallest and largest radii used.
102
+ * **Calculation**: Standard statistical measures on the array of radii.
103
+ * **Gen 19 Result**:
104
+ * `mean_radius`: ~0.0738
105
+ * `std_dev_radius`: ~0.0259
106
+ * `min_radius`: ~0.0187
107
+ * `max_radius`: ~0.1026
108
+ (Indicates a variety of circle sizes are being used)
109
+
110
+ **Current Generation (Gen 19) Analysis & Recommendations:**
111
+
112
+ * **Status**: The solution for Generation 19 is `valid` according to the `overlap_score` and `boundary_violation_score` (both are 0.0). This means it adheres to the strict rules of non-overlapping and in-bounds circles.
113
+ * **Evolution Stage**: Given the primary score of 1.9201 (presumably we are trying to minimize it), and the validity of the solution, the evolution process is likely in an **OPTIMIZATION** or **CONVERGENCE** stage. The early **EXPLORATION** phase of finding *any* valid packing seems to have passed.
114
+ * **Insights**: The non-zero `std_dev_radius` suggests the current solution is not using uniform circles, which is a common strategy for denser packing.
115
+ * **Recommendations**:
116
+ * **For Optimization/Refinement**: Continue to focus on reducing the `primary_score` (sum of radii).
117
+ * **For Stagnation/Plateau**: If the `primary_score` stagnates, these auxiliary metrics can help diagnose.
118
+ * If `overlap_score` or `boundary_violation_score` start to increase (even slightly above 0 for new solutions), it means the evolver is drifting towards invalid solutions. This might indicate that the penalty for invalid solutions is not strong enough, or the search space is pushing towards locally optimal but invalid configurations. The *degree* of violation can be used as a soft penalty.
119
+ * Monitor `radius_statistics` for changes in diversity. If `std_dev_radius` approaches 0, it might indicate a loss of solution diversity, potentially leading to local optima. Encouraging a wider range of radii could lead to new packing arrangements.
120
+ * **Future Metric Idea**: Consider a "packing efficiency" metric that calculates the total area covered by the circles (`sum(pi * r^2)`) relative to the unit square area. This provides a direct measure of how "full" the square is. This can help distinguish between solutions that achieve a low `sum_of_radii` by using many tiny circles versus fewer, larger circles. This would be a good metric to add if we are trying to maximize the actual filled area.
121
+ * The code for these auxiliary metrics is located at `/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory/aux_metrics.py`.
122
+
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory_backup_20260202_200614/aux_metrics.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import numpy as np
3
+ import os
4
+ import json
5
+
6
+ def calculate_overlap_score(centers: np.ndarray, radii: np.ndarray, atol: float = 1e-6) -> float:
7
+ """
8
+ Calculates a score representing the total "depth" of overlap between circles.
9
+ A score of 0 means no overlap (within tolerance). Higher scores indicate more overlap.
10
+ """
11
+ num_circles = centers.shape[0]
12
+ overlap_score = 0.0
13
+ for i in range(num_circles):
14
+ for j in range(i + 1, num_circles):
15
+ dist = np.linalg.norm(centers[i] - centers[j])
16
+ potential_overlap = radii[i] + radii[j] - dist
17
+ if potential_overlap > atol: # Only count positive overlap
18
+ overlap_score += potential_overlap
19
+ return overlap_score
20
+
21
+ def calculate_boundary_violation_score(centers: np.ndarray, radii: np.ndarray, atol: float = 1e-6) -> float:
22
+ """
23
+ Calculates a score representing how much circles extend beyond the unit square [0, 1] x [0, 1].
24
+ A score of 0 means no boundary violation (within tolerance). Higher scores indicate more violation.
25
+ """
26
+ boundary_violation_score = 0.0
27
+ for i in range(centers.shape[0]):
28
+ x, y = centers[i]
29
+ r = radii[i]
30
+
31
+ # Check left boundary (x - r < 0)
32
+ if x - r < -atol:
33
+ boundary_violation_score += abs(x - r)
34
+
35
+ # Check right boundary (x + r > 1)
36
+ if x + r > 1 + atol:
37
+ boundary_violation_score += (x + r - 1)
38
+
39
+ # Check bottom boundary (y - r < 0)
40
+ if y - r < -atol:
41
+ boundary_violation_score += abs(y - r)
42
+
43
+ # Check top boundary (y + r > 1)
44
+ if y + r > 1 + atol:
45
+ boundary_violation_score += (y + r - 1)
46
+
47
+ return boundary_violation_score
48
+
49
+ def calculate_radius_statistics(radii: np.ndarray) -> dict:
50
+ """
51
+ Calculates statistics about the radii of the circles.
52
+ """
53
+ if len(radii) == 0:
54
+ return {"mean_radius": 0.0, "std_dev_radius": 0.0, "min_radius": 0.0, "max_radius": 0.0}
55
+ return {
56
+ "mean_radius": float(np.mean(radii)),
57
+ "std_dev_radius": float(np.std(radii)),
58
+ "min_radius": float(np.min(radii)),
59
+ "max_radius": float(np.max(radii)),
60
+ }
61
+
62
+ def evaluate_auxiliary_metrics(generation_path: str) -> dict:
63
+ """
64
+ Loads data from a generation's extra.npz and calculates auxiliary metrics.
65
+ """
66
+ extra_npz_path = os.path.join(generation_path, "results", "extra.npz")
67
+
68
+ # Also get primary score for context
69
+ metrics_json_path = os.path.join(generation_path, "results", "metrics.json")
70
+ primary_score = None
71
+ try:
72
+ with open(metrics_json_path, 'r') as f:
73
+ metrics_data = json.load(f)
74
+ primary_score = metrics_data.get("combined_score")
75
+ except Exception as e:
76
+ print(f"Error reading metrics.json: {e}")
77
+
78
+ if not os.path.exists(extra_npz_path):
79
+ print(f"Warning: extra.npz not found at {extra_npz_path}. Cannot calculate auxiliary metrics.")
80
+ return {
81
+ "primary_score": primary_score,
82
+ "aux_metrics": {
83
+ "overlap_score": -1, # Indicates missing data
84
+ "boundary_violation_score": -1,
85
+ "radius_statistics": {},
86
+ },
87
+ "diagnostics": {"status": "missing_extra_npz"},
88
+ }
89
+
90
+ try:
91
+ data = np.load(extra_npz_path)
92
+ centers = data["centers"]
93
+ radii = data["radii"]
94
+ except Exception as e:
95
+ print(f"Error loading extra.npz: {e}")
96
+ return {
97
+ "primary_score": primary_score,
98
+ "aux_metrics": {
99
+ "overlap_score": -1,
100
+ "boundary_violation_score": -1,
101
+ "radius_statistics": {},
102
+ },
103
+ "diagnostics": {"status": f"error_loading_extra_npz: {e}"},
104
+ }
105
+
106
+ overlap = calculate_overlap_score(centers, radii)
107
+ boundary_violation = calculate_boundary_violation_score(centers, radii)
108
+ radius_stats = calculate_radius_statistics(radii)
109
+
110
+ return {
111
+ "primary_score": primary_score,
112
+ "aux_metrics": {
113
+ "overlap_score": overlap,
114
+ "boundary_violation_score": boundary_violation,
115
+ "radius_statistics": radius_stats,
116
+ },
117
+ "diagnostics": {"status": "success"},
118
+ }
119
+
120
+ if __name__ == "__main__":
121
+ # Example usage (for testing purposes)
122
+ # Replace with actual gen path or mock data for testing
123
+ current_gen_path = "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_19"
124
+
125
+ # Create dummy extra.npz and metrics.json for testing if they don't exist
126
+ if not os.path.exists(os.path.join(current_gen_path, "results", "extra.npz")):
127
+ print("Creating dummy extra.npz for testing...")
128
+ dummy_centers = np.array([[0.2, 0.2], [0.8, 0.8]])
129
+ dummy_radii = np.array([0.15, 0.15])
130
+ # Make them overlap
131
+ dummy_centers_overlap = np.array([[0.2, 0.2], [0.3, 0.2]])
132
+ dummy_radii_overlap = np.array([0.1, 0.1])
133
+
134
+ # Make one go out of bounds
135
+ dummy_centers_oob = np.array([[0.05, 0.05], [0.95, 0.95]])
136
+ dummy_radii_oob = np.array([0.1, 0.1])
137
+
138
+ os.makedirs(os.path.join(current_gen_path, "results"), exist_ok=True)
139
+ np.savez(os.path.join(current_gen_path, "results", "extra.npz"),
140
+ centers=dummy_centers_oob, radii=dummy_radii_oob, reported_sum=np.sum(dummy_radii_oob))
141
+
142
+ if not os.path.exists(os.path.join(current_gen_path, "results", "metrics.json")):
143
+ print("Creating dummy metrics.json for testing...")
144
+ dummy_metrics = {"combined_score": 0.3}
145
+ with open(os.path.join(current_gen_path, "results", "metrics.json"), 'w') as f:
146
+ json.dump(dummy_metrics, f)
147
+
148
+ results = evaluate_auxiliary_metrics(current_gen_path)
149
+ print(json.dumps(results, indent=2))
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory_backup_20260202_200614/auxiliary_metrics.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import numpy as np
3
+ import os
4
+
5
+ def calculate_code_complexity(file_path: str) -> int:
6
+ """Calculates the number of non-empty, non-comment lines of code."""
7
+ line_count = 0
8
+ try:
9
+ with open(file_path, 'r') as f:
10
+ for line in f:
11
+ stripped_line = line.strip()
12
+ if stripped_line and not stripped_line.startswith('#'):
13
+ line_count += 1
14
+ except FileNotFoundError:
15
+ return -1 # Indicate error
16
+ return line_count
17
+
18
+ def calculate_overlap_magnitude(centers: np.ndarray, radii: np.ndarray, atol: float = 1e-6) -> float:
19
+ """
20
+ Calculates the maximum overlap magnitude between any two circles.
21
+ Returns 0.0 if no overlap, or the maximum overlap distance if overlaps exist.
22
+ """
23
+ n_circles = centers.shape[0]
24
+ max_overlap = 0.0
25
+
26
+ for i in range(n_circles):
27
+ for j in range(i + 1, n_circles):
28
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
29
+ # Overlap occurs if distance is less than sum of radii
30
+ overlap = (radii[i] + radii[j]) - dist
31
+ if overlap > max_overlap:
32
+ max_overlap = overlap
33
+
34
+ # Only return positive overlap, otherwise return 0.0 if no overlap or tiny negative due to precision
35
+ return max_overlap if max_overlap > atol else 0.0
36
+
37
+ def calculate_radii_std_dev(radii: np.ndarray) -> float:
38
+ """Calculates the standard deviation of the radii."""
39
+ if len(radii) == 0:
40
+ return 0.0
41
+ return float(np.std(radii))
42
+
43
+ def load_packing_data(results_dir: str):
44
+ """Loads centers and radii from extra.npz."""
45
+ extra_file = os.path.join(results_dir, "extra.npz")
46
+ try:
47
+ data = np.load(extra_file)
48
+ return data['centers'], data['radii']
49
+ except FileNotFoundError:
50
+ # print(f"Error: {extra_file} not found.")
51
+ return None, None
52
+ except Exception as e:
53
+ # print(f"Error loading {extra_file}: {e}")
54
+ return None, None
55
+
56
+ def run_auxiliary_metrics(gen_path: str, results_dir: str):
57
+ """
58
+ Runs all auxiliary metrics for a given generation.
59
+ gen_path: Path to the generation directory (e.g., /.../gen_9/)
60
+ results_dir: Path to the results directory within the generation (e.g., /.../gen_9/results/)
61
+ """
62
+ metrics = {}
63
+
64
+ # Metric 1: Code Complexity
65
+ main_py_path = os.path.join(gen_path, "main.py")
66
+ metrics["code_lines"] = calculate_code_complexity(main_py_path)
67
+
68
+ # Metric 2 & 3: Overlap Magnitude and Radii Std Dev
69
+ centers, radii = load_packing_data(results_dir)
70
+ if centers is not None and radii is not None:
71
+ metrics["max_overlap_magnitude"] = calculate_overlap_magnitude(centers, radii)
72
+ metrics["radii_std_dev"] = calculate_radii_std_dev(radii)
73
+ else:
74
+ metrics["max_overlap_magnitude"] = -1.0 # Indicate error
75
+ metrics["radii_std_dev"] = -1.0 # Indicate error
76
+
77
+ return metrics
78
+
79
+ if __name__ == "__main__":
80
+ # Example usage (replace with actual paths for testing)
81
+ # This block will not be executed by the agent, but is useful for local testing if needed.
82
+ current_gen_path = "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_9"
83
+ current_results_dir = "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_9/results"
84
+
85
+ aux_metrics = run_auxiliary_metrics(current_gen_path, current_results_dir)
86
+ print("Auxiliary Metrics:", aux_metrics)
87
+
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory_backup_20260202_200614/run_aux_metrics_temp.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import sys
3
+ import os
4
+
5
+ # Add the directory containing auxiliary_metrics.py to the Python path
6
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
7
+
8
+ import auxiliary_metrics
9
+
10
+ current_gen_path = "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_9"
11
+ current_results_dir = "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_9/results"
12
+
13
+ aux_metrics = auxiliary_metrics.run_auxiliary_metrics(current_gen_path, current_results_dir)
14
+ print("Auxiliary Metrics:", aux_metrics)
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/eval_agent_memory_backup_20260202_200614/service_state.json ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "generation_history": [
3
+ {
4
+ "generation": 0,
5
+ "primary_score": 2.4,
6
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
7
+ "timestamp": 1770061173.8938816
8
+ },
9
+ {
10
+ "generation": 1,
11
+ "primary_score": 2.4051,
12
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
13
+ "timestamp": 1770061173.9999762
14
+ },
15
+ {
16
+ "generation": 2,
17
+ "primary_score": 2.4101999999999997,
18
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
19
+ "timestamp": 1770061174.1054802
20
+ },
21
+ {
22
+ "generation": 3,
23
+ "primary_score": 2.4153000000000002,
24
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
25
+ "timestamp": 1770061174.2107964
26
+ },
27
+ {
28
+ "generation": 4,
29
+ "primary_score": 2.4204,
30
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
31
+ "timestamp": 1770061174.3160756
32
+ },
33
+ {
34
+ "generation": 5,
35
+ "primary_score": 2.4255,
36
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
37
+ "timestamp": 1770061174.4213583
38
+ },
39
+ {
40
+ "generation": 6,
41
+ "primary_score": 2.4305999999999996,
42
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
43
+ "timestamp": 1770061174.5267565
44
+ },
45
+ {
46
+ "generation": 7,
47
+ "primary_score": 2.4357,
48
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
49
+ "timestamp": 1770061174.6322458
50
+ },
51
+ {
52
+ "generation": 8,
53
+ "primary_score": 2.4408,
54
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
55
+ "timestamp": 1770061174.7379289
56
+ },
57
+ {
58
+ "generation": 9,
59
+ "primary_score": 2.4459,
60
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
61
+ "timestamp": 1770061174.843402
62
+ },
63
+ {
64
+ "generation": 10,
65
+ "primary_score": 2.4509999999999996,
66
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
67
+ "timestamp": 1770061411.445956
68
+ },
69
+ {
70
+ "generation": 11,
71
+ "primary_score": 2.4561,
72
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
73
+ "timestamp": 1770061411.5518658
74
+ },
75
+ {
76
+ "generation": 12,
77
+ "primary_score": 2.4612,
78
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
79
+ "timestamp": 1770061411.6626825
80
+ },
81
+ {
82
+ "generation": 13,
83
+ "primary_score": 2.4663,
84
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
85
+ "timestamp": 1770061411.7682931
86
+ },
87
+ {
88
+ "generation": 14,
89
+ "primary_score": 2.4713999999999996,
90
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
91
+ "timestamp": 1770061411.8733294
92
+ },
93
+ {
94
+ "generation": 15,
95
+ "primary_score": 2.4765,
96
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
97
+ "timestamp": 1770061411.978649
98
+ },
99
+ {
100
+ "generation": 16,
101
+ "primary_score": 2.4816,
102
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
103
+ "timestamp": 1770061412.0836413
104
+ },
105
+ {
106
+ "generation": 17,
107
+ "primary_score": 2.4867,
108
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
109
+ "timestamp": 1770061412.1885157
110
+ },
111
+ {
112
+ "generation": 18,
113
+ "primary_score": 2.4917999999999996,
114
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
115
+ "timestamp": 1770061412.2932863
116
+ },
117
+ {
118
+ "generation": 19,
119
+ "primary_score": 2.4969,
120
+ "results_dir": "examples/circle_packing/results/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215",
121
+ "timestamp": 1770061412.3980374
122
+ }
123
+ ],
124
+ "last_agent_trigger_gen": 19,
125
+ "total_notifications": 20,
126
+ "total_agent_runs": 2,
127
+ "last_update": 1770062750.14768
128
+ }
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/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__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_1/edit.diff ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,94 +1,105 @@
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
+ + This version uses a grid-like packing with 5 rows of circles,
16
+ + with counts [5, 5, 6, 5, 5] to accommodate 26 circles. This
17
+ + improves packing density and uniformity over the previous concentric
18
+ + ring design.
19
+ +
20
+ Returns:
21
+ - Tuple of (centers, radii, sum_of_radii)
22
+ + Tuple of (centers, radii)
23
+ centers: np.array of shape (26, 2) with (x, y) coordinates
24
+ radii: np.array of shape (26) with radius of each circle
25
+ - sum_of_radii: Sum of all radii
26
+ """
27
+ - # Initialize arrays for 26 circles
28
+ n = 26
29
+ centers = np.zeros((n, 2))
30
+ + count = 0
31
+
32
+ - # Place circles in a structured pattern
33
+ - # This is a simple pattern - evolution will improve this
34
+ + # Based on a 6-circle wide row, the diameter is 1/6. Radius is 1/12.
35
+ + r = 1.0 / 12.0
36
+ + diameter = 2.0 * r
37
+
38
+ - # First, place a large circle in the center
39
+ - centers[0] = [0.5, 0.5]
40
+ + # Define y-coordinates for 5 rows, centered in the square
41
+ + y_total_height = 5 * diameter
42
+ + y_margin = (1.0 - y_total_height) / 2.0
43
+ + y_coords = [y_margin + r + i * diameter for i in range(5)]
44
+
45
+ - # Place 8 circles around it in a ring
46
+ - for i in range(8):
47
+ - angle = 2 * np.pi * i / 8
48
+ - centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
49
+ + # Define x-coordinates for rows with 5 and 6 circles
50
+ + x_coords_6 = [r + i * diameter for i in range(6)]
51
+ + x_total_width_5 = 5 * diameter
52
+ + x_margin_5 = (1.0 - x_total_width_5) / 2.0
53
+ + x_coords_5 = [x_margin_5 + r + i * diameter for i in range(5)]
54
+
55
+ - # Place 16 more circles in an outer ring
56
+ - for i in range(16):
57
+ - angle = 2 * np.pi * i / 16
58
+ - centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
59
+ + row_configs = [x_coords_5, x_coords_5, x_coords_6, x_coords_5, x_coords_5]
60
+
61
+ - # Additional positioning adjustment to make sure all circles
62
+ - # are inside the square and don't overlap
63
+ - # Clip to ensure everything is inside the unit square
64
+ - centers = np.clip(centers, 0.01, 0.99)
65
+ + for i, x_coords in enumerate(row_configs):
66
+ + y = y_coords[i]
67
+ + for x in x_coords:
68
+ + if count < n:
69
+ + centers[count] = [x, y]
70
+ + count += 1
71
+
72
+ - # Compute maximum valid radii for this configuration
73
+ radii = compute_max_radii(centers)
74
+ return centers, radii
75
+
76
+
77
+ def compute_max_radii(centers):
78
+ """
79
+ Compute the maximum possible radii for each circle position
80
+ such that they don't overlap and stay within the unit square.
81
+
82
+ Args:
83
+ centers: np.array of shape (n, 2) with (x, y) coordinates
84
+
85
+ Returns:
86
+ np.array of shape (n) with radius of each circle
87
+ """
88
+ n = centers.shape[0]
89
+ radii = np.ones(n)
90
+
91
+ # First, limit by distance to square borders
92
+ for i in range(n):
93
+ x, y = centers[i]
94
+ # Distance to borders
95
+ radii[i] = min(x, y, 1 - x, 1 - y)
96
+
97
+ - # Then, limit by distance to other circles
98
+ - # Each pair of circles with centers at distance d can have
99
+ - # sum of radii at most d to avoid overlap
100
+ - for i in range(n):
101
+ - for j in range(i + 1, n):
102
+ - dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
103
+ + # Iteratively adjust radii to resolve overlaps until convergence
104
+ + for _ in range(100): # Max 100 iterations to prevent infinite loops
105
+ + changed = False
106
+ + for i in range(n):
107
+ + for j in range(i + 1, n):
108
+ + dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
109
+
110
+ - # If current radii would cause overlap
111
+ - if radii[i] + radii[j] > dist:
112
+ - # Scale both radii proportionally
113
+ - scale = dist / (radii[i] + radii[j])
114
+ - radii[i] *= scale
115
+ - radii[j] *= scale
116
+ + # If current radii would cause overlap (with a small tolerance)
117
+ + if radii[i] + radii[j] > dist + 1e-9:
118
+ + # Scale both radii proportionally
119
+ + if radii[i] + radii[j] > 0:
120
+ + scale = dist / (radii[i] + radii[j])
121
+ + radii[i] *= scale
122
+ + radii[j] *= scale
123
+ + changed = True
124
+ +
125
+ + if not changed:
126
+ + break # Exit if no changes were made in a full pass
127
+
128
+ return radii
129
+
130
+
131
+ # EVOLVE-BLOCK-END
132
+
133
+
134
+ # This part remains fixed (not evolved)
135
+ def run_packing():
136
+ """Run the circle packing constructor for n=26"""
137
+ centers, radii = construct_packing()
138
+ # Calculate the sum of radii
139
+ sum_radii = np.sum(radii)
140
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_1/main.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ This version uses a grid-like packing with 5 rows of circles,
13
+ with counts [5, 5, 6, 5, 5] to accommodate 26 circles. This
14
+ improves packing density and uniformity over the previous concentric
15
+ ring design.
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
+ centers = np.zeros((n, 2))
24
+ count = 0
25
+
26
+ # Based on a 6-circle wide row, the diameter is 1/6. Radius is 1/12.
27
+ r = 1.0 / 12.0
28
+ diameter = 2.0 * r
29
+
30
+ # Define y-coordinates for 5 rows, centered in the square
31
+ y_total_height = 5 * diameter
32
+ y_margin = (1.0 - y_total_height) / 2.0
33
+ y_coords = [y_margin + r + i * diameter for i in range(5)]
34
+
35
+ # Define x-coordinates for rows with 5 and 6 circles
36
+ x_coords_6 = [r + i * diameter for i in range(6)]
37
+ x_total_width_5 = 5 * diameter
38
+ x_margin_5 = (1.0 - x_total_width_5) / 2.0
39
+ x_coords_5 = [x_margin_5 + r + i * diameter for i in range(5)]
40
+
41
+ row_configs = [x_coords_5, x_coords_5, x_coords_6, x_coords_5, x_coords_5]
42
+
43
+ for i, x_coords in enumerate(row_configs):
44
+ y = y_coords[i]
45
+ for x in x_coords:
46
+ if count < n:
47
+ centers[count] = [x, y]
48
+ count += 1
49
+
50
+ radii = compute_max_radii(centers)
51
+ return centers, radii
52
+
53
+
54
+ def compute_max_radii(centers):
55
+ """
56
+ Compute the maximum possible radii for each circle position
57
+ such that they don't overlap and stay within the unit square.
58
+
59
+ Args:
60
+ centers: np.array of shape (n, 2) with (x, y) coordinates
61
+
62
+ Returns:
63
+ np.array of shape (n) with radius of each circle
64
+ """
65
+ n = centers.shape[0]
66
+ radii = np.ones(n)
67
+
68
+ # First, limit by distance to square borders
69
+ for i in range(n):
70
+ x, y = centers[i]
71
+ # Distance to borders
72
+ radii[i] = min(x, y, 1 - x, 1 - y)
73
+
74
+ # Iteratively adjust radii to resolve overlaps until convergence
75
+ for _ in range(100): # Max 100 iterations to prevent infinite loops
76
+ changed = False
77
+ for i in range(n):
78
+ for j in range(i + 1, n):
79
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
80
+
81
+ # If current radii would cause overlap (with a small tolerance)
82
+ if radii[i] + radii[j] > dist + 1e-9:
83
+ # Scale both radii proportionally
84
+ if radii[i] + radii[j] > 0:
85
+ scale = dist / (radii[i] + radii[j])
86
+ radii[i] *= scale
87
+ radii[j] *= scale
88
+ changed = True
89
+
90
+ if not changed:
91
+ break # Exit if no changes were made in a full pass
92
+
93
+ return radii
94
+
95
+
96
+ # EVOLVE-BLOCK-END
97
+
98
+
99
+ # This part remains fixed (not evolved)
100
+ def run_packing():
101
+ """Run the circle packing constructor for n=26"""
102
+ centers, radii = construct_packing()
103
+ # Calculate the sum of radii
104
+ sum_radii = np.sum(radii)
105
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/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__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_1/search_replace.txt ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ I have analyzed the current circle packing implementation, its performance metrics, and the accompanying visualization. The feedback clearly indicates that the primary weaknesses are extremely low space utilization (high `gap_analysis` and low `packing_efficiency`) and an unbalanced packing density (`density_variance`). This is caused by a flawed constructor that places circles in concentric rings, with the outermost ring being pushed to the edges of the square by an aggressive `clip` operation, resulting in tiny, ineffective circles.
2
+
3
+ My improvement strategy consists of two independent but complementary changes:
4
+
5
+ 1. **Replace the Constructor**: I will replace the inefficient ring-based constructor with a much more uniform grid-based approach. The new constructor arranges the 26 circles into 5 rows with a `[5, 5, 6, 5, 5]` configuration. This structure is designed to fill the unit square evenly, directly addressing the core issues of unused space and density variance. This change alone is expected to more than double the primary score (sum of radii).
6
+
7
+ 2. **Improve Radii Calculation**: The existing `compute_max_radii` function uses a single-pass approach that is not guaranteed to find the optimal radii for a given set of centers. I will replace it with an iterative relaxation algorithm. This new method repeatedly adjusts radii to resolve overlaps until the configuration stabilizes, ensuring a better local optimum and maximizing the radii for the provided center positions.
8
+
9
+ These two changes work together to first create a vastly superior initial layout and then to fine-tune the radii within that layout for a maximal sum.
10
+
11
+ <NAME>
12
+ grid_based_constructor
13
+ </NAME>
14
+
15
+ <DESCRIPTION>
16
+ The original concentric ring constructor is highly inefficient. It creates a very non-uniform packing with large empty spaces, as confirmed by the low packing_efficiency (0.25) and gap_analysis (0.25) scores. The outer ring of circles is placed such that the `np.clip` operation forces them to the edges, resulting in minuscule radii and a low overall sum.
17
+
18
+ To address this, I am replacing the flawed constructor with a new one that arranges the 26 circles in a more uniform grid-like pattern. Specifically, it uses 5 rows of circles with a [5, 5, 6, 5, 5] configuration. This layout is designed to fill the unit square much more evenly.
19
+
20
+ The theoretical sum of radii for this configuration is approximately 2.167 (26 * 1/12), which is more than double the current score of 0.96. This change directly targets the "unused space" and "density variance" issues highlighted in the feedback. I've also corrected the docstring to accurately reflect the function's return values.
21
+ </DESCRIPTION>
22
+
23
+ <DIFF>
24
+ <<<<<<< SEARCH
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
+ def construct_packing():
66
+ """
67
+ Construct a specific arrangement of 26 circles in a unit square
68
+ that attempts to maximize the sum of their radii.
69
+
70
+ This version uses a grid-like packing with 5 rows of circles,
71
+ with counts [5, 5, 6, 5, 5] to accommodate 26 circles. This
72
+ improves packing density and uniformity over the previous concentric
73
+ ring design.
74
+
75
+ Returns:
76
+ Tuple of (centers, radii)
77
+ centers: np.array of shape (26, 2) with (x, y) coordinates
78
+ radii: np.array of shape (26) with radius of each circle
79
+ """
80
+ n = 26
81
+ centers = np.zeros((n, 2))
82
+ count = 0
83
+
84
+ # Based on a 6-circle wide row, the diameter is 1/6. Radius is 1/12.
85
+ r = 1.0 / 12.0
86
+ diameter = 2.0 * r
87
+
88
+ # Define y-coordinates for 5 rows, centered in the square
89
+ y_total_height = 5 * diameter
90
+ y_margin = (1.0 - y_total_height) / 2.0
91
+ y_coords = [y_margin + r + i * diameter for i in range(5)]
92
+
93
+ # Define x-coordinates for rows with 5 and 6 circles
94
+ x_coords_6 = [r + i * diameter for i in range(6)]
95
+ x_total_width_5 = 5 * diameter
96
+ x_margin_5 = (1.0 - x_total_width_5) / 2.0
97
+ x_coords_5 = [x_margin_5 + r + i * diameter for i in range(5)]
98
+
99
+ row_configs = [x_coords_5, x_coords_5, x_coords_6, x_coords_5, x_coords_5]
100
+
101
+ for i, x_coords in enumerate(row_configs):
102
+ y = y_coords[i]
103
+ for x in x_coords:
104
+ if count < n:
105
+ centers[count] = [x, y]
106
+ count += 1
107
+
108
+ radii = compute_max_radii(centers)
109
+ return centers, radii
110
+ >>>>>>> REPLACE
111
+ </DIFF>
112
+ <NAME>
113
+ iterative_radii_computation
114
+ </NAME>
115
+
116
+ <DESCRIPTION>
117
+ The current `compute_max_radii` function performs a single pass over all pairs of circles to resolve overlaps. This is not guaranteed to find the maximal radii for a given set of centers. The final radii can depend on the order of comparisons, and shrinking one pair can open up space for another pair that was checked earlier, but this space is never reclaimed.
118
+
119
+ I am replacing this with an iterative algorithm. The new implementation repeatedly scans for and resolves overlaps until no more changes occur. This relaxation method allows the radii to settle into a more optimal, stable configuration for the given center positions, ensuring a better local maximum for the sum of radii. I've also added a small tolerance to handle floating point inaccuracies. This will improve the sum of radii for any given center configuration.
120
+ </DESCRIPTION>
121
+
122
+ <DIFF>
123
+ <<<<<<< SEARCH
124
+ def compute_max_radii(centers):
125
+ """
126
+ Compute the maximum possible radii for each circle position
127
+ such that they don't overlap and stay within the unit square.
128
+
129
+ Args:
130
+ centers: np.array of shape (n, 2) with (x, y) coordinates
131
+
132
+ Returns:
133
+ np.array of shape (n) with radius of each circle
134
+ """
135
+ n = centers.shape[0]
136
+ radii = np.ones(n)
137
+
138
+ # First, limit by distance to square borders
139
+ for i in range(n):
140
+ x, y = centers[i]
141
+ # Distance to borders
142
+ radii[i] = min(x, y, 1 - x, 1 - y)
143
+
144
+ # Then, limit by distance to other circles
145
+ # Each pair of circles with centers at distance d can have
146
+ # sum of radii at most d to avoid overlap
147
+ for i in range(n):
148
+ for j in range(i + 1, n):
149
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
150
+
151
+ # If current radii would cause overlap
152
+ if radii[i] + radii[j] > dist:
153
+ # Scale both radii proportionally
154
+ scale = dist / (radii[i] + radii[j])
155
+ radii[i] *= scale
156
+ radii[j] *= scale
157
+
158
+ return radii
159
+ =======
160
+ def compute_max_radii(centers):
161
+ """
162
+ Compute the maximum possible radii for each circle position
163
+ such that they don't overlap and stay within the unit square.
164
+
165
+ Args:
166
+ centers: np.array of shape (n, 2) with (x, y) coordinates
167
+
168
+ Returns:
169
+ np.array of shape (n) with radius of each circle
170
+ """
171
+ n = centers.shape[0]
172
+ radii = np.ones(n)
173
+
174
+ # First, limit by distance to square borders
175
+ for i in range(n):
176
+ x, y = centers[i]
177
+ # Distance to borders
178
+ radii[i] = min(x, y, 1 - x, 1 - y)
179
+
180
+ # Iteratively adjust radii to resolve overlaps until convergence
181
+ for _ in range(100): # Max 100 iterations to prevent infinite loops
182
+ changed = False
183
+ for i in range(n):
184
+ for j in range(i + 1, n):
185
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
186
+
187
+ # If current radii would cause overlap (with a small tolerance)
188
+ if radii[i] + radii[j] > dist + 1e-9:
189
+ # Scale both radii proportionally
190
+ if radii[i] + radii[j] > 0:
191
+ scale = dist / (radii[i] + radii[j])
192
+ radii[i] *= scale
193
+ radii[j] *= scale
194
+ changed = True
195
+
196
+ if not changed:
197
+ break # Exit if no changes were made in a full pass
198
+
199
+ return radii
200
+ >>>>>>> REPLACE
201
+ </DIFF>
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_10/edit.diff ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,101 +1,96 @@
4
+ # EVOLVE-BLOCK-START
5
+ """Constructor-based circle packing for n=26 circles"""
6
+
7
+ import numpy as np
8
+ +from itertools import product
9
+
10
+
11
+ def construct_packing():
12
+ """
13
+ - Construct a specific arrangement of 26 circles in a unit square
14
+ - that attempts to maximize the sum of their radii.
15
+ + Constructs an arrangement of 26 circles based on the highly effective
16
+ + 5x5 grid structure, which is optimal for 25 circles, and adds a 26th
17
+ + circle into a central interstitial space. This approach directly
18
+ + addresses feedback on poor corner and edge utilization from previous
19
+ + concentric models.
20
+
21
+ Returns:
22
+ - Tuple of (centers, radii, sum_of_radii)
23
+ + Tuple of (centers, radii)
24
+ centers: np.array of shape (26, 2) with (x, y) coordinates
25
+ radii: np.array of shape (26) with radius of each circle
26
+ - sum_of_radii: Sum of all radii
27
+ """
28
+ - # Initialize arrays for 26 circles
29
+ n = 26
30
+ centers = np.zeros((n, 2))
31
+
32
+ - # Place circles in a structured pattern
33
+ - # This is a simple pattern - evolution will improve this
34
+ + # Create 25 centers on a 5x5 grid. This is the known optimal packing for n=25.
35
+ + # The grid coordinates are spaced to fill the unit square perfectly with
36
+ + # circles of radius 0.1, centered at [0.1, 0.3, 0.5, 0.7, 0.9].
37
+ + coords = np.linspace(0.1, 0.9, 5)
38
+ + grid_centers = np.array(list(product(coords, coords)))
39
+ + centers[:25] = grid_centers
40
+
41
+ - # Place central circle
42
+ - centers[0] = [0.5, 0.5]
43
+ + # Place the 26th circle in one of the four central interstitial gaps.
44
+ + # These gaps are the most spacious. We choose the one at (0.4, 0.4).
45
+ + centers[25] = [0.4, 0.4]
46
+
47
+ - # Place 9 circles in an inner ring (total 1 + 9 = 10)
48
+ - num_inner_ring = 9
49
+ - inner_ring_radius_multiplier = 0.25
50
+ - for i in range(num_inner_ring):
51
+ - angle = 2 * np.pi * i / num_inner_ring
52
+ - centers[i + 1] = [0.5 + inner_ring_radius_multiplier * np.cos(angle),
53
+ - 0.5 + inner_ring_radius_multiplier * np.sin(angle)]
54
+ -
55
+ - # Place 16 circles in an outer ring (total 1 + 9 + 16 = 26)
56
+ - num_outer_ring = 16
57
+ - outer_ring_radius_multiplier = 0.47 # Increased to push circles closer to boundary
58
+ - for i in range(num_outer_ring):
59
+ - angle = 2 * np.pi * i / num_outer_ring
60
+ - centers[i + 1 + num_inner_ring] = [0.5 + outer_ring_radius_multiplier * np.cos(angle),
61
+ - 0.5 + outer_ring_radius_multiplier * np.sin(angle)]
62
+ -
63
+ - # Additional positioning adjustment to make sure all circles
64
+ - # are inside the square and don't overlap
65
+ - # The compute_max_radii function already handles boundary constraints.
66
+ - # Removing clipping allows circles to properly utilize the square's edges.
67
+ - # centers = np.clip(centers, 0.01, 0.99)
68
+ -
69
+ - # Compute maximum valid radii for this configuration
70
+ + # Compute the maximum radii for this optimized initial configuration.
71
+ radii = compute_max_radii(centers)
72
+ return centers, radii
73
+
74
+
75
+ def compute_max_radii(centers):
76
+ """
77
+ - Compute the maximum possible radii for each circle position
78
+ - such that they don't overlap and stay within the unit square.
79
+ + Compute the maximum possible radii for a given set of circle centers.
80
+ + This function uses an iterative relaxation method to ensure no circles
81
+ + overlap and all circles remain within the unit square. The iterative
82
+ + loop is essential for convergence to a valid, locally optimal solution.
83
+
84
+ Args:
85
+ centers: np.array of shape (n, 2) with (x, y) coordinates
86
+
87
+ Returns:
88
+ - np.array of shape (n) with radius of each circle
89
+ + np.array of shape (n) with the radius of each circle.
90
+ """
91
+ n = centers.shape[0]
92
+ - radii = np.ones(n)
93
+ + radii = np.zeros(n)
94
+
95
+ - # First, limit by distance to square borders
96
+ + # Initialize radii based on the distance to the square's boundaries.
97
+ for i in range(n):
98
+ x, y = centers[i]
99
+ - # Distance to borders
100
+ - radii[i] = min(x, y, 1 - x, 1 - y)
101
+ + radii[i] = min(x, 1 - x, y, 1 - y)
102
+
103
+ - # Then, limit by distance to other circles
104
+ - # Each pair of circles with centers at distance d can have
105
+ - # sum of radii at most d to avoid overlap
106
+ - for i in range(n):
107
+ - for j in range(i + 1, n):
108
+ - dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
109
+ + # Iteratively adjust radii to resolve all overlaps until the packing is stable.
110
+ + # This loop is crucial for finding a valid solution.
111
+ + for _ in range(200): # Increased iterations for better convergence
112
+ + changed = False
113
+ + for i in range(n):
114
+ + for j in range(i + 1, n):
115
+ + dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
116
+
117
+ - # If current radii would cause overlap
118
+ - if radii[i] + radii[j] > dist:
119
+ - # Scale both radii proportionally
120
+ - scale = dist / (radii[i] + radii[j])
121
+ - radii[i] *= scale
122
+ - radii[j] *= scale
123
+ + # If circles overlap (with a small tolerance for floating point errors)
124
+ + if radii[i] + radii[j] > dist + 1e-12:
125
+ + # Scale both radii down proportionally to resolve the overlap.
126
+ + # This method is stable and converges well.
127
+ + total_radius = radii[i] + radii[j]
128
+ + if total_radius > 0:
129
+ + scale = dist / total_radius
130
+ + radii[i] *= scale
131
+ + radii[j] *= scale
132
+ + changed = True
133
+ +
134
+ + if not changed:
135
+ + # If a full pass is made with no changes, the configuration is stable.
136
+ + break
137
+
138
+ return radii
139
+
140
+
141
+ # EVOLVE-BLOCK-END
142
+
143
+
144
+ # This part remains fixed (not evolved)
145
+ def run_packing():
146
+ """Run the circle packing constructor for n=26"""
147
+ centers, radii = construct_packing()
148
+ # Calculate the sum of radii
149
+ sum_radii = np.sum(radii)
150
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_10/main.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """Constructor-based circle packing for n=26 circles"""
3
+
4
+ import numpy as np
5
+ from itertools import product
6
+
7
+
8
+ def construct_packing():
9
+ """
10
+ Constructs an arrangement of 26 circles based on the highly effective
11
+ 5x5 grid structure, which is optimal for 25 circles, and adds a 26th
12
+ circle into a central interstitial space. This approach directly
13
+ addresses feedback on poor corner and edge utilization from previous
14
+ concentric models.
15
+
16
+ Returns:
17
+ Tuple of (centers, radii)
18
+ centers: np.array of shape (26, 2) with (x, y) coordinates
19
+ radii: np.array of shape (26) with radius of each circle
20
+ """
21
+ n = 26
22
+ centers = np.zeros((n, 2))
23
+
24
+ # Create 25 centers on a 5x5 grid. This is the known optimal packing for n=25.
25
+ # The grid coordinates are spaced to fill the unit square perfectly with
26
+ # circles of radius 0.1, centered at [0.1, 0.3, 0.5, 0.7, 0.9].
27
+ coords = np.linspace(0.1, 0.9, 5)
28
+ grid_centers = np.array(list(product(coords, coords)))
29
+ centers[:25] = grid_centers
30
+
31
+ # Place the 26th circle in one of the four central interstitial gaps.
32
+ # These gaps are the most spacious. We choose the one at (0.4, 0.4).
33
+ centers[25] = [0.4, 0.4]
34
+
35
+ # Compute the maximum radii for this optimized initial configuration.
36
+ radii = compute_max_radii(centers)
37
+ return centers, radii
38
+
39
+
40
+ def compute_max_radii(centers):
41
+ """
42
+ Compute the maximum possible radii for a given set of circle centers.
43
+ This function uses an iterative relaxation method to ensure no circles
44
+ overlap and all circles remain within the unit square. The iterative
45
+ loop is essential for convergence to a valid, locally optimal solution.
46
+
47
+ Args:
48
+ centers: np.array of shape (n, 2) with (x, y) coordinates
49
+
50
+ Returns:
51
+ np.array of shape (n) with the radius of each circle.
52
+ """
53
+ n = centers.shape[0]
54
+ radii = np.zeros(n)
55
+
56
+ # Initialize radii based on the distance to the square's boundaries.
57
+ for i in range(n):
58
+ x, y = centers[i]
59
+ radii[i] = min(x, 1 - x, y, 1 - y)
60
+
61
+ # Iteratively adjust radii to resolve all overlaps until the packing is stable.
62
+ # This loop is crucial for finding a valid solution.
63
+ for _ in range(200): # Increased iterations for better convergence
64
+ changed = False
65
+ for i in range(n):
66
+ for j in range(i + 1, n):
67
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
68
+
69
+ # If circles overlap (with a small tolerance for floating point errors)
70
+ if radii[i] + radii[j] > dist + 1e-12:
71
+ # Scale both radii down proportionally to resolve the overlap.
72
+ # This method is stable and converges well.
73
+ total_radius = radii[i] + radii[j]
74
+ if total_radius > 0:
75
+ scale = dist / total_radius
76
+ radii[i] *= scale
77
+ radii[j] *= scale
78
+ changed = True
79
+
80
+ if not changed:
81
+ # If a full pass is made with no changes, the configuration is stable.
82
+ break
83
+
84
+ return radii
85
+
86
+
87
+ # EVOLVE-BLOCK-END
88
+
89
+
90
+ # This part remains fixed (not evolved)
91
+ def run_packing():
92
+ """Run the circle packing constructor for n=26"""
93
+ centers, radii = construct_packing()
94
+ # Calculate the sum of radii
95
+ sum_radii = np.sum(radii)
96
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_10/original.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # Place central circle
26
+ centers[0] = [0.5, 0.5]
27
+
28
+ # Place 9 circles in an inner ring (total 1 + 9 = 10)
29
+ num_inner_ring = 9
30
+ inner_ring_radius_multiplier = 0.25
31
+ for i in range(num_inner_ring):
32
+ angle = 2 * np.pi * i / num_inner_ring
33
+ centers[i + 1] = [0.5 + inner_ring_radius_multiplier * np.cos(angle),
34
+ 0.5 + inner_ring_radius_multiplier * np.sin(angle)]
35
+
36
+ # Place 16 circles in an outer ring (total 1 + 9 + 16 = 26)
37
+ num_outer_ring = 16
38
+ outer_ring_radius_multiplier = 0.47 # Increased to push circles closer to boundary
39
+ for i in range(num_outer_ring):
40
+ angle = 2 * np.pi * i / num_outer_ring
41
+ centers[i + 1 + num_inner_ring] = [0.5 + outer_ring_radius_multiplier * np.cos(angle),
42
+ 0.5 + outer_ring_radius_multiplier * np.sin(angle)]
43
+
44
+ # Additional positioning adjustment to make sure all circles
45
+ # are inside the square and don't overlap
46
+ # The compute_max_radii function already handles boundary constraints.
47
+ # Removing clipping allows circles to properly utilize the square's edges.
48
+ # centers = np.clip(centers, 0.01, 0.99)
49
+
50
+ # Compute maximum valid radii for this configuration
51
+ radii = compute_max_radii(centers)
52
+ return centers, radii
53
+
54
+
55
+ def compute_max_radii(centers):
56
+ """
57
+ Compute the maximum possible radii for each circle position
58
+ such that they don't overlap and stay within the unit square.
59
+
60
+ Args:
61
+ centers: np.array of shape (n, 2) with (x, y) coordinates
62
+
63
+ Returns:
64
+ np.array of shape (n) with radius of each circle
65
+ """
66
+ n = centers.shape[0]
67
+ radii = np.ones(n)
68
+
69
+ # First, limit by distance to square borders
70
+ for i in range(n):
71
+ x, y = centers[i]
72
+ # Distance to borders
73
+ radii[i] = min(x, y, 1 - x, 1 - y)
74
+
75
+ # Then, limit by distance to other circles
76
+ # Each pair of circles with centers at distance d can have
77
+ # sum of radii at most d to avoid overlap
78
+ for i in range(n):
79
+ for j in range(i + 1, n):
80
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
81
+
82
+ # If current radii would cause overlap
83
+ if radii[i] + radii[j] > dist:
84
+ # Scale both radii proportionally
85
+ scale = dist / (radii[i] + radii[j])
86
+ radii[i] *= scale
87
+ radii[j] *= scale
88
+
89
+ return radii
90
+
91
+
92
+ # EVOLVE-BLOCK-END
93
+
94
+
95
+ # This part remains fixed (not evolved)
96
+ def run_packing():
97
+ """Run the circle packing constructor for n=26"""
98
+ centers, radii = construct_packing()
99
+ # Calculate the sum of radii
100
+ sum_radii = np.sum(radii)
101
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_10/search_replace.txt ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <NAME>
2
+ revert_to_proven_grid_packing
3
+ </NAME>
4
+
5
+ <DESCRIPTION>
6
+ The current implementation uses a concentric ring pattern and a non-iterative radius calculation, resulting in a low score of 1.6273 and poor metrics (especially corner utilization: 0.185, and gap analysis: 0.388). This change reverts to the strategy from the best-performing prior solution (score 1.9201) to address these issues.
7
+
8
+ This involves two key improvements:
9
+ 1. **Placement Strategy:** The concentric ring pattern is replaced with a 5x5 grid plus one interstitial circle. This structure inherently utilizes the entire square, including corners and edges, directly addressing the feedback about poor boundary utilization and large unused space.
10
+ 2. **Radius Calculation:** The flawed, single-pass `compute_max_radii` is replaced with a correct iterative relaxation algorithm. The new function iterates until the radii converge, ensuring a valid and locally-optimal packing, which is critical for maximizing the sum of radii.
11
+
12
+ This combined change holistically targets the weaknesses identified in the feedback and restores a proven, high-performance architecture.
13
+ </DESCRIPTION>
14
+
15
+ <DIFF>
16
+ <<<<<<< SEARCH
17
+ import numpy as np
18
+
19
+
20
+ def construct_packing():
21
+ """
22
+ Construct a specific arrangement of 26 circles in a unit square
23
+ that attempts to maximize the sum of their radii.
24
+
25
+ Returns:
26
+ Tuple of (centers, radii, sum_of_radii)
27
+ centers: np.array of shape (26, 2) with (x, y) coordinates
28
+ radii: np.array of shape (26) with radius of each circle
29
+ sum_of_radii: Sum of all radii
30
+ """
31
+ # Initialize arrays for 26 circles
32
+ n = 26
33
+ centers = np.zeros((n, 2))
34
+
35
+ # Place circles in a structured pattern
36
+ # This is a simple pattern - evolution will improve this
37
+
38
+ # Place central circle
39
+ centers[0] = [0.5, 0.5]
40
+
41
+ # Place 9 circles in an inner ring (total 1 + 9 = 10)
42
+ num_inner_ring = 9
43
+ inner_ring_radius_multiplier = 0.25
44
+ for i in range(num_inner_ring):
45
+ angle = 2 * np.pi * i / num_inner_ring
46
+ centers[i + 1] = [0.5 + inner_ring_radius_multiplier * np.cos(angle),
47
+ 0.5 + inner_ring_radius_multiplier * np.sin(angle)]
48
+
49
+ # Place 16 circles in an outer ring (total 1 + 9 + 16 = 26)
50
+ num_outer_ring = 16
51
+ outer_ring_radius_multiplier = 0.47 # Increased to push circles closer to boundary
52
+ for i in range(num_outer_ring):
53
+ angle = 2 * np.pi * i / num_outer_ring
54
+ centers[i + 1 + num_inner_ring] = [0.5 + outer_ring_radius_multiplier * np.cos(angle),
55
+ 0.5 + outer_ring_radius_multiplier * np.sin(angle)]
56
+
57
+ # Additional positioning adjustment to make sure all circles
58
+ # are inside the square and don't overlap
59
+ # The compute_max_radii function already handles boundary constraints.
60
+ # Removing clipping allows circles to properly utilize the square's edges.
61
+ # centers = np.clip(centers, 0.01, 0.99)
62
+
63
+ # Compute maximum valid radii for this configuration
64
+ radii = compute_max_radii(centers)
65
+ return centers, radii
66
+
67
+
68
+ def compute_max_radii(centers):
69
+ """
70
+ Compute the maximum possible radii for each circle position
71
+ such that they don't overlap and stay within the unit square.
72
+
73
+ Args:
74
+ centers: np.array of shape (n, 2) with (x, y) coordinates
75
+
76
+ Returns:
77
+ np.array of shape (n) with radius of each circle
78
+ """
79
+ n = centers.shape[0]
80
+ radii = np.ones(n)
81
+
82
+ # First, limit by distance to square borders
83
+ for i in range(n):
84
+ x, y = centers[i]
85
+ # Distance to borders
86
+ radii[i] = min(x, y, 1 - x, 1 - y)
87
+
88
+ # Then, limit by distance to other circles
89
+ # Each pair of circles with centers at distance d can have
90
+ # sum of radii at most d to avoid overlap
91
+ for i in range(n):
92
+ for j in range(i + 1, n):
93
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
94
+
95
+ # If current radii would cause overlap
96
+ if radii[i] + radii[j] > dist:
97
+ # Scale both radii proportionally
98
+ scale = dist / (radii[i] + radii[j])
99
+ radii[i] *= scale
100
+ radii[j] *= scale
101
+
102
+ return radii
103
+ =======
104
+ import numpy as np
105
+ from itertools import product
106
+
107
+
108
+ def construct_packing():
109
+ """
110
+ Constructs an arrangement of 26 circles based on the highly effective
111
+ 5x5 grid structure, which is optimal for 25 circles, and adds a 26th
112
+ circle into a central interstitial space. This approach directly
113
+ addresses feedback on poor corner and edge utilization from previous
114
+ concentric models.
115
+
116
+ Returns:
117
+ Tuple of (centers, radii)
118
+ centers: np.array of shape (26, 2) with (x, y) coordinates
119
+ radii: np.array of shape (26) with radius of each circle
120
+ """
121
+ n = 26
122
+ centers = np.zeros((n, 2))
123
+
124
+ # Create 25 centers on a 5x5 grid. This is the known optimal packing for n=25.
125
+ # The grid coordinates are spaced to fill the unit square perfectly with
126
+ # circles of radius 0.1, centered at [0.1, 0.3, 0.5, 0.7, 0.9].
127
+ coords = np.linspace(0.1, 0.9, 5)
128
+ grid_centers = np.array(list(product(coords, coords)))
129
+ centers[:25] = grid_centers
130
+
131
+ # Place the 26th circle in one of the four central interstitial gaps.
132
+ # These gaps are the most spacious. We choose the one at (0.4, 0.4).
133
+ centers[25] = [0.4, 0.4]
134
+
135
+ # Compute the maximum radii for this optimized initial configuration.
136
+ radii = compute_max_radii(centers)
137
+ return centers, radii
138
+
139
+
140
+ def compute_max_radii(centers):
141
+ """
142
+ Compute the maximum possible radii for a given set of circle centers.
143
+ This function uses an iterative relaxation method to ensure no circles
144
+ overlap and all circles remain within the unit square. The iterative
145
+ loop is essential for convergence to a valid, locally optimal solution.
146
+
147
+ Args:
148
+ centers: np.array of shape (n, 2) with (x, y) coordinates
149
+
150
+ Returns:
151
+ np.array of shape (n) with the radius of each circle.
152
+ """
153
+ n = centers.shape[0]
154
+ radii = np.zeros(n)
155
+
156
+ # Initialize radii based on the distance to the square's boundaries.
157
+ for i in range(n):
158
+ x, y = centers[i]
159
+ radii[i] = min(x, 1 - x, y, 1 - y)
160
+
161
+ # Iteratively adjust radii to resolve all overlaps until the packing is stable.
162
+ # This loop is crucial for finding a valid solution.
163
+ for _ in range(200): # Increased iterations for better convergence
164
+ changed = False
165
+ for i in range(n):
166
+ for j in range(i + 1, n):
167
+ dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
168
+
169
+ # If circles overlap (with a small tolerance for floating point errors)
170
+ if radii[i] + radii[j] > dist + 1e-12:
171
+ # Scale both radii down proportionally to resolve the overlap.
172
+ # This method is stable and converges well.
173
+ total_radius = radii[i] + radii[j]
174
+ if total_radius > 0:
175
+ scale = dist / total_radius
176
+ radii[i] *= scale
177
+ radii[j] *= scale
178
+ changed = True
179
+
180
+ if not changed:
181
+ # If a full pass is made with no changes, the configuration is stable.
182
+ break
183
+
184
+ return radii
185
+ >>>>>>> REPLACE
186
+ </DIFF>
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_100/edit.diff ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,327 +1,356 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+ from itertools import product
7
+
8
+
9
+ class CirclePacker:
10
+ """
11
+ A class to construct circle packings within a unit square using a hybrid
12
+ optimization approach. It combines an exhaustive search for initial placement
13
+ with a localized refinement stage.
14
+ """
15
+ def __init__(self, num_circles=26):
16
+ self.n = num_circles
17
+ self.centers = np.zeros((self.n, 2))
18
+ self.radii = np.zeros(self.n)
19
+
20
+ @staticmethod
21
+ def _compute_max_radii_static(centers, n):
22
+ """
23
+ Computes the maximum possible radii for a given set of circle centers using
24
+ an iterative growth and constraint resolution method. This static version
25
+ incorporates an adaptive growth factor and dynamic tolerance for enhanced
26
+ performance and precision, based on the best prior implementation.
27
+
28
+ Args:
29
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
30
+ n (int): Number of circles.
31
+
32
+ Returns:
33
+ np.array: An array of shape (n) containing the final radius of each circle.
34
+ """
35
+ radii = np.zeros(n)
36
+
37
+ # Parameters for adaptive growth factor and dynamic tolerance
38
+ growth_factor_initial = 1.005
39
+ growth_factor_final = 1.002
40
+ tolerance_initial = 1e-7
41
+ tolerance_final = 1e-10
42
+
43
+ outer_iterations = 400
44
+ inner_iterations = 20
45
+
46
+ # Initialize radii based on the distance to the square's boundaries.
47
+ for i in range(n):
48
+ x, y = centers[i]
49
+ radii[i] = min(x, 1 - x, y, 1 - y)
50
+
51
+ for outer_iter_idx in range(outer_iterations):
52
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0 + 1e-9)
53
+ # Use exponential decay for smoother, more effective parameter transition, based on prior high-scoring versions.
54
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**interp_factor
55
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**interp_factor
56
+
57
+ radii *= current_growth_factor # Tentatively grow all radii
58
+
59
+ for _inner_iter_idx in range(inner_iterations):
60
+ constraints_changed = False
61
+
62
+ # Enforce boundary constraints with dynamic tolerance
63
+ for i in range(n):
64
+ x, y = centers[i]
65
+ boundary_limit = min(x, 1 - x, y, 1 - y)
66
+ if radii[i] > boundary_limit + current_tolerance:
67
+ radii[i] = boundary_limit
68
+ constraints_changed = True
69
+
70
+ # Resolve overlaps between circles with dynamic tolerance
71
+ for i in range(n):
72
+ for j in range(i + 1, n):
73
+ dist = np.linalg.norm(centers[i] - centers[j])
74
+ if radii[i] + radii[j] > dist + current_tolerance:
75
+ total_radius = radii[i] + radii[j]
76
+ if total_radius > tolerance_final:
77
+ scale = dist / total_radius
78
+ radii[i] *= scale
79
+ radii[j] *= scale
80
+ constraints_changed = True
81
+
82
+ if not constraints_changed:
83
+ break
84
+ return radii
85
+
86
+ def _initial_grid_placement(self):
87
+ """
88
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
89
+ """
90
+ coords = np.linspace(0.1, 0.9, 5)
91
+ grid_centers = np.array(list(product(coords, coords)))
92
+ self.centers[:25] = grid_centers
93
+
94
+ def _find_optimal_26th_circle_position(self):
95
+ """
96
+ Performs a multi-resolution grid search to find the best initial position
97
+ for the 26th circle among a dense set of interstitial candidates.
98
+ """
99
+ base_25_centers = np.copy(self.centers[:25])
100
+
101
+ # Initialize with a default position (center of the square) and calculate its sum of radii
102
+ optimal_26th_pos = np.array([0.5, 0.5])
103
+ trial_centers_initial = np.vstack([base_25_centers, optimal_26th_pos])
104
+ trial_radii_initial = CirclePacker._compute_max_radii_static(trial_centers_initial, self.n)
105
+ best_sum_radii = np.sum(trial_radii_initial)
106
+
107
+ # Keep track of the best position from the coarse search to center the fine search
108
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
109
+
110
+ - interstitial_core_coords = np.linspace(0.2, 0.8, 4)
111
+ + # Expand the coarse search grid to cover a broader area, as per recommendations.
112
+ + interstitial_core_coords = np.linspace(0.1, 0.9, 8)
113
+
114
+ # --- Phase 1: Coarse Grid Search ---
115
+ # Explore a broader region first to identify promising areas.
116
+ coarse_delta = 0.05
117
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta])
118
+
119
+ coarse_candidate_points = []
120
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
121
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets):
122
+ coarse_candidate_points.append([base_x + offset_x, base_y + offset_y])
123
+
124
+ for candidate_pos in coarse_candidate_points:
125
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
126
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
127
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
128
+ current_sum_radii = np.sum(trial_radii)
129
+
130
+ if current_sum_radii > best_sum_radii:
131
+ best_sum_radii = current_sum_radii
132
+ optimal_26th_pos = clipped_candidate_pos
133
+ best_coarse_pos_for_fine_tuning = clipped_candidate_pos
134
+
135
+ # --- Phase 2: Fine Grid Search around the best coarse position ---
136
+ # Focus the search more precisely around the most promising area identified in Phase 1.
137
+ fine_delta = 0.01
138
+ fine_perturbation_offsets = np.array([-fine_delta, 0, fine_delta])
139
+
140
+ fine_candidate_points = []
141
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets):
142
+ fine_candidate_points.append([best_coarse_pos_for_fine_tuning[0] + offset_x, best_coarse_pos_for_fine_tuning[1] + offset_y])
143
+
144
+ for candidate_pos in fine_candidate_points:
145
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
146
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
147
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
148
+ current_sum_radii = np.sum(trial_radii)
149
+
150
+ if current_sum_radii > best_sum_radii:
151
+ best_sum_radii = current_sum_radii
152
+ optimal_26th_pos = clipped_candidate_pos
153
+
154
+ self.centers[25] = optimal_26th_pos
155
+
156
+ return self.centers, best_sum_radii
157
+
158
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
159
+ """
160
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
161
+ positions of a cluster of circles: the 26th and its nearest neighbors.
162
+ This allows the base grid to relax and better accommodate the interstitial circle.
163
+ + This version is enhanced with stress-based selection and dynamic step size adjustment.
164
+ """
165
+ current_centers = np.copy(initial_centers)
166
+ - current_sum_radii = initial_sum_radii
167
+ + # Compute radii for initial stress calculation and consistent sum.
168
+ + current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
169
+ + current_sum_radii = np.sum(current_radii)
170
+
171
+ best_centers_local = np.copy(current_centers)
172
+ best_sum_radii_local = current_sum_radii
173
+
174
+ # SA parameters adapted from high-performing prior implementations for fine-tuning
175
+ num_iterations = 150
176
+ initial_step_size = 0.005
177
+ initial_temp = 0.0001
178
+ cooling_rate = 0.99
179
+
180
+ step_size = initial_step_size
181
+ temp = initial_temp
182
+
183
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
184
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
185
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
186
+ cluster_indices = np.append(closest_neighbor_indices, 25)
187
+
188
+ - for _ in range(num_iterations):
189
+ - # Select a random circle from the cluster to perturb
190
+ - idx_to_move = np.random.choice(cluster_indices)
191
+ + # Parameters for dynamic step size adjustment
192
+ + acceptance_window = 30
193
+ + acceptance_count = 0
194
+ + target_acceptance_rate = 0.44
195
+ + adjustment_factor = 1.05
196
+ +
197
+ + for i in range(num_iterations):
198
+ + # Stress-based selection within the cluster
199
+ + cluster_radii = current_radii[cluster_indices]
200
+ + inv_radii = 1.0 / (cluster_radii + 1e-9)
201
+ + weights = (inv_radii - np.min(inv_radii))**2
202
+ + if np.sum(weights) > 1e-9:
203
+ + probabilities = weights / np.sum(weights)
204
+ + idx_to_move = np.random.choice(cluster_indices, p=probabilities)
205
+ + else:
206
+ + idx_to_move = np.random.choice(cluster_indices)
207
+ +
208
+
209
+ trial_centers = np.copy(current_centers)
210
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
211
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
212
+
213
+ # Evaluate the new configuration
214
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
215
+ trial_sum_radii = np.sum(trial_radii)
216
+
217
+ # Metropolis-Hastings acceptance criterion
218
+ delta_energy = trial_sum_radii - current_sum_radii
219
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
220
+ current_centers = trial_centers
221
+ + current_radii = trial_radii # Update radii for stress calculation
222
+ current_sum_radii = trial_sum_radii
223
+ + acceptance_count += 1
224
+
225
+ if current_sum_radii > best_sum_radii_local:
226
+ best_sum_radii_local = current_sum_radii
227
+ best_centers_local = np.copy(current_centers)
228
+ +
229
+ + # Dynamic step size adjustment
230
+ + if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
231
+ + acceptance_rate = acceptance_count / acceptance_window
232
+ + if acceptance_rate > target_acceptance_rate:
233
+ + step_size *= adjustment_factor
234
+ + else:
235
+ + step_size /= adjustment_factor
236
+ + acceptance_count = 0
237
+
238
+ temp *= cooling_rate
239
+ step_size = max(step_size * cooling_rate, 1e-7)
240
+
241
+ return best_centers_local, best_sum_radii_local
242
+
243
+ def _global_refinement_sa(self, initial_centers):
244
+ """
245
+ Applies a global, low-temperature Simulated Annealing search to fine-tune
246
+ the positions of ALL circles, acting as a "gentle jiggle" phase to
247
+ settle the entire packing into a better local optimum. This technique was
248
+ present in prior high-scoring implementations.
249
+ """
250
+ # SA parameters adapted from high-performing prior implementations for a thorough yet gentle search.
251
+ sa_iterations = 300
252
+ sa_initial_temp = 5e-6
253
+ sa_cooling_rate = 0.99
254
+ sa_initial_step_size = 0.001
255
+
256
+ current_centers = np.copy(initial_centers)
257
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
258
+ current_sum_radii = np.sum(current_radii)
259
+
260
+ best_centers = np.copy(current_centers)
261
+ best_sum_radii = current_sum_radii
262
+
263
+ temp = sa_initial_temp
264
+ step_size = sa_initial_step_size
265
+
266
+ all_indices = np.arange(self.n)
267
+
268
+ # Parameters for dynamic step size adjustment
269
+ acceptance_window = 30
270
+ acceptance_count = 0
271
+ target_acceptance_rate = 0.44 # Common target for SA
272
+ adjustment_factor = 1.05
273
+
274
+ for i in range(sa_iterations):
275
+ # Stress-based selection: prioritize moving circles with smaller radii.
276
+ inv_radii = 1.0 / (current_radii + 1e-9)
277
+ weights = (inv_radii - np.min(inv_radii))**2
278
+ if np.sum(weights) > 1e-9:
279
+ probabilities = weights / np.sum(weights)
280
+ idx_to_move = np.random.choice(all_indices, p=probabilities)
281
+ else:
282
+ idx_to_move = np.random.choice(all_indices)
283
+
284
+ trial_centers = np.copy(current_centers)
285
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
286
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
287
+
288
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
289
+ trial_sum_radii = np.sum(trial_radii)
290
+
291
+ delta_energy = trial_sum_radii - current_sum_radii
292
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
293
+ current_centers = trial_centers
294
+ current_radii = trial_radii # Keep radii in sync for stress calculation
295
+ current_sum_radii = trial_sum_radii
296
+ acceptance_count += 1
297
+
298
+ if current_sum_radii > best_sum_radii:
299
+ best_sum_radii = current_sum_radii
300
+ best_centers = np.copy(current_centers)
301
+
302
+ # Periodically adjust step_size based on acceptance rate to balance exploration/exploitation.
303
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
304
+ acceptance_rate = acceptance_count / acceptance_window
305
+ if acceptance_rate > target_acceptance_rate:
306
+ step_size *= adjustment_factor
307
+ else:
308
+ step_size /= adjustment_factor
309
+ acceptance_count = 0
310
+
311
+ temp *= sa_cooling_rate
312
+ step_size = max(step_size * sa_cooling_rate, 1e-8) # Maintain overall cooling trend for step size
313
+
314
+ return best_centers
315
+
316
+ def construct_packing(self):
317
+ """
318
+ Main method to construct the circle packing, orchestrating a multi-stage
319
+ optimization: initial placement, multi-res search, local SA, and global SA.
320
+ """
321
+ self._initial_grid_placement()
322
+
323
+ if self.n > 25:
324
+ # Stage 1: Multi-resolution search for the 26th circle's initial position
325
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
326
+
327
+ # Stage 2: Local refinement of the interstitial circle and its neighbors using SA
328
+ centers_after_local_sa, _ = self._local_refinement_cluster_sa(
329
+ centers_after_search,
330
+ sum_radii_after_search
331
+ )
332
+
333
+ # Stage 3: Global "gentle jiggle" refinement on all circles using SA
334
+ centers_after_global_sa = self._global_refinement_sa(centers_after_local_sa)
335
+ self.centers = centers_after_global_sa
336
+
337
+ # Final radius calculation for the fully optimized center configuration
338
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
339
+ return self.centers, self.radii
340
+
341
+
342
+ def construct_packing():
343
+ """
344
+ Constructs an arrangement of 26 circles by leveraging a superior three-stage
345
+ optimization strategy: initial grid, dense interstitial search, and localized SA.
346
+
347
+ Returns:
348
+ Tuple of (centers, radii)
349
+ centers: np.array of shape (26, 2) with (x, y) coordinates
350
+ radii: np.array of shape (26) with final radius of each circle
351
+ """
352
+ packer = CirclePacker(num_circles=26)
353
+ centers, radii = packer.construct_packing()
354
+ return centers, radii
355
+ # EVOLVE-BLOCK-END
356
+
357
+
358
+ # This part remains fixed (not evolved)
359
+ def run_packing():
360
+ """Run the circle packing constructor for n=26"""
361
+ centers, radii = construct_packing()
362
+ # Calculate the sum of radii
363
+ sum_radii = np.sum(radii)
364
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_100/main.py ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings within a unit square using a hybrid
9
+ optimization approach. It combines an exhaustive search for initial placement
10
+ with a localized refinement stage.
11
+ """
12
+ def __init__(self, num_circles=26):
13
+ self.n = num_circles
14
+ self.centers = np.zeros((self.n, 2))
15
+ self.radii = np.zeros(self.n)
16
+
17
+ @staticmethod
18
+ def _compute_max_radii_static(centers, n):
19
+ """
20
+ Computes the maximum possible radii for a given set of circle centers using
21
+ an iterative growth and constraint resolution method. This static version
22
+ incorporates an adaptive growth factor and dynamic tolerance for enhanced
23
+ performance and precision, based on the best prior implementation.
24
+
25
+ Args:
26
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
27
+ n (int): Number of circles.
28
+
29
+ Returns:
30
+ np.array: An array of shape (n) containing the final radius of each circle.
31
+ """
32
+ radii = np.zeros(n)
33
+
34
+ # Parameters for adaptive growth factor and dynamic tolerance
35
+ growth_factor_initial = 1.005
36
+ growth_factor_final = 1.002
37
+ tolerance_initial = 1e-7
38
+ tolerance_final = 1e-10
39
+
40
+ outer_iterations = 400
41
+ inner_iterations = 20
42
+
43
+ # Initialize radii based on the distance to the square's boundaries.
44
+ for i in range(n):
45
+ x, y = centers[i]
46
+ radii[i] = min(x, 1 - x, y, 1 - y)
47
+
48
+ for outer_iter_idx in range(outer_iterations):
49
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0 + 1e-9)
50
+ # Use exponential decay for smoother, more effective parameter transition, based on prior high-scoring versions.
51
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**interp_factor
52
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**interp_factor
53
+
54
+ radii *= current_growth_factor # Tentatively grow all radii
55
+
56
+ for _inner_iter_idx in range(inner_iterations):
57
+ constraints_changed = False
58
+
59
+ # Enforce boundary constraints with dynamic tolerance
60
+ for i in range(n):
61
+ x, y = centers[i]
62
+ boundary_limit = min(x, 1 - x, y, 1 - y)
63
+ if radii[i] > boundary_limit + current_tolerance:
64
+ radii[i] = boundary_limit
65
+ constraints_changed = True
66
+
67
+ # Resolve overlaps between circles with dynamic tolerance
68
+ for i in range(n):
69
+ for j in range(i + 1, n):
70
+ dist = np.linalg.norm(centers[i] - centers[j])
71
+ if radii[i] + radii[j] > dist + current_tolerance:
72
+ total_radius = radii[i] + radii[j]
73
+ if total_radius > tolerance_final:
74
+ scale = dist / total_radius
75
+ radii[i] *= scale
76
+ radii[j] *= scale
77
+ constraints_changed = True
78
+
79
+ if not constraints_changed:
80
+ break
81
+ return radii
82
+
83
+ def _initial_grid_placement(self):
84
+ """
85
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
86
+ """
87
+ coords = np.linspace(0.1, 0.9, 5)
88
+ grid_centers = np.array(list(product(coords, coords)))
89
+ self.centers[:25] = grid_centers
90
+
91
+ def _find_optimal_26th_circle_position(self):
92
+ """
93
+ Performs a multi-resolution grid search to find the best initial position
94
+ for the 26th circle among a dense set of interstitial candidates.
95
+ """
96
+ base_25_centers = np.copy(self.centers[:25])
97
+
98
+ # Initialize with a default position (center of the square) and calculate its sum of radii
99
+ optimal_26th_pos = np.array([0.5, 0.5])
100
+ trial_centers_initial = np.vstack([base_25_centers, optimal_26th_pos])
101
+ trial_radii_initial = CirclePacker._compute_max_radii_static(trial_centers_initial, self.n)
102
+ best_sum_radii = np.sum(trial_radii_initial)
103
+
104
+ # Keep track of the best position from the coarse search to center the fine search
105
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
106
+
107
+ # Expand the coarse search grid to cover a broader area, as per recommendations.
108
+ interstitial_core_coords = np.linspace(0.1, 0.9, 8)
109
+
110
+ # --- Phase 1: Coarse Grid Search ---
111
+ # Explore a broader region first to identify promising areas.
112
+ coarse_delta = 0.05
113
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta])
114
+
115
+ coarse_candidate_points = []
116
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
117
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets):
118
+ coarse_candidate_points.append([base_x + offset_x, base_y + offset_y])
119
+
120
+ for candidate_pos in coarse_candidate_points:
121
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
122
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
123
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
124
+ current_sum_radii = np.sum(trial_radii)
125
+
126
+ if current_sum_radii > best_sum_radii:
127
+ best_sum_radii = current_sum_radii
128
+ optimal_26th_pos = clipped_candidate_pos
129
+ best_coarse_pos_for_fine_tuning = clipped_candidate_pos
130
+
131
+ # --- Phase 2: Fine Grid Search around the best coarse position ---
132
+ # Focus the search more precisely around the most promising area identified in Phase 1.
133
+ fine_delta = 0.01
134
+ fine_perturbation_offsets = np.array([-fine_delta, 0, fine_delta])
135
+
136
+ fine_candidate_points = []
137
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets):
138
+ fine_candidate_points.append([best_coarse_pos_for_fine_tuning[0] + offset_x, best_coarse_pos_for_fine_tuning[1] + offset_y])
139
+
140
+ for candidate_pos in fine_candidate_points:
141
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
142
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
143
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
144
+ current_sum_radii = np.sum(trial_radii)
145
+
146
+ if current_sum_radii > best_sum_radii:
147
+ best_sum_radii = current_sum_radii
148
+ optimal_26th_pos = clipped_candidate_pos
149
+
150
+ self.centers[25] = optimal_26th_pos
151
+
152
+ return self.centers, best_sum_radii
153
+
154
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
155
+ """
156
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
157
+ positions of a cluster of circles: the 26th and its nearest neighbors.
158
+ This allows the base grid to relax and better accommodate the interstitial circle.
159
+ This version is enhanced with stress-based selection and dynamic step size adjustment.
160
+ """
161
+ current_centers = np.copy(initial_centers)
162
+ # Compute radii for initial stress calculation and consistent sum.
163
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
164
+ current_sum_radii = np.sum(current_radii)
165
+
166
+ best_centers_local = np.copy(current_centers)
167
+ best_sum_radii_local = current_sum_radii
168
+
169
+ # SA parameters adapted from high-performing prior implementations for fine-tuning
170
+ num_iterations = 150
171
+ initial_step_size = 0.005
172
+ initial_temp = 0.0001
173
+ cooling_rate = 0.99
174
+
175
+ step_size = initial_step_size
176
+ temp = initial_temp
177
+
178
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
179
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
180
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
181
+ cluster_indices = np.append(closest_neighbor_indices, 25)
182
+
183
+ # Parameters for dynamic step size adjustment
184
+ acceptance_window = 30
185
+ acceptance_count = 0
186
+ target_acceptance_rate = 0.44
187
+ adjustment_factor = 1.05
188
+
189
+ for i in range(num_iterations):
190
+ # Stress-based selection within the cluster
191
+ cluster_radii = current_radii[cluster_indices]
192
+ inv_radii = 1.0 / (cluster_radii + 1e-9)
193
+ weights = (inv_radii - np.min(inv_radii))**2
194
+ if np.sum(weights) > 1e-9:
195
+ probabilities = weights / np.sum(weights)
196
+ idx_to_move = np.random.choice(cluster_indices, p=probabilities)
197
+ else:
198
+ idx_to_move = np.random.choice(cluster_indices)
199
+
200
+
201
+ trial_centers = np.copy(current_centers)
202
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
203
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
204
+
205
+ # Evaluate the new configuration
206
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
207
+ trial_sum_radii = np.sum(trial_radii)
208
+
209
+ # Metropolis-Hastings acceptance criterion
210
+ delta_energy = trial_sum_radii - current_sum_radii
211
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
212
+ current_centers = trial_centers
213
+ current_radii = trial_radii # Update radii for stress calculation
214
+ current_sum_radii = trial_sum_radii
215
+ acceptance_count += 1
216
+
217
+ if current_sum_radii > best_sum_radii_local:
218
+ best_sum_radii_local = current_sum_radii
219
+ best_centers_local = np.copy(current_centers)
220
+
221
+ # Dynamic step size adjustment
222
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
223
+ acceptance_rate = acceptance_count / acceptance_window
224
+ if acceptance_rate > target_acceptance_rate:
225
+ step_size *= adjustment_factor
226
+ else:
227
+ step_size /= adjustment_factor
228
+ acceptance_count = 0
229
+
230
+ temp *= cooling_rate
231
+ step_size = max(step_size * cooling_rate, 1e-7)
232
+
233
+ return best_centers_local, best_sum_radii_local
234
+
235
+ def _global_refinement_sa(self, initial_centers):
236
+ """
237
+ Applies a global, low-temperature Simulated Annealing search to fine-tune
238
+ the positions of ALL circles, acting as a "gentle jiggle" phase to
239
+ settle the entire packing into a better local optimum. This technique was
240
+ present in prior high-scoring implementations.
241
+ """
242
+ # SA parameters adapted from high-performing prior implementations for a thorough yet gentle search.
243
+ sa_iterations = 300
244
+ sa_initial_temp = 5e-6
245
+ sa_cooling_rate = 0.99
246
+ sa_initial_step_size = 0.001
247
+
248
+ current_centers = np.copy(initial_centers)
249
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
250
+ current_sum_radii = np.sum(current_radii)
251
+
252
+ best_centers = np.copy(current_centers)
253
+ best_sum_radii = current_sum_radii
254
+
255
+ temp = sa_initial_temp
256
+ step_size = sa_initial_step_size
257
+
258
+ all_indices = np.arange(self.n)
259
+
260
+ # Parameters for dynamic step size adjustment
261
+ acceptance_window = 30
262
+ acceptance_count = 0
263
+ target_acceptance_rate = 0.44 # Common target for SA
264
+ adjustment_factor = 1.05
265
+
266
+ for i in range(sa_iterations):
267
+ # Stress-based selection: prioritize moving circles with smaller radii.
268
+ inv_radii = 1.0 / (current_radii + 1e-9)
269
+ weights = (inv_radii - np.min(inv_radii))**2
270
+ if np.sum(weights) > 1e-9:
271
+ probabilities = weights / np.sum(weights)
272
+ idx_to_move = np.random.choice(all_indices, p=probabilities)
273
+ else:
274
+ idx_to_move = np.random.choice(all_indices)
275
+
276
+ trial_centers = np.copy(current_centers)
277
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
278
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
279
+
280
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
281
+ trial_sum_radii = np.sum(trial_radii)
282
+
283
+ delta_energy = trial_sum_radii - current_sum_radii
284
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
285
+ current_centers = trial_centers
286
+ current_radii = trial_radii # Keep radii in sync for stress calculation
287
+ current_sum_radii = trial_sum_radii
288
+ acceptance_count += 1
289
+
290
+ if current_sum_radii > best_sum_radii:
291
+ best_sum_radii = current_sum_radii
292
+ best_centers = np.copy(current_centers)
293
+
294
+ # Periodically adjust step_size based on acceptance rate to balance exploration/exploitation.
295
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
296
+ acceptance_rate = acceptance_count / acceptance_window
297
+ if acceptance_rate > target_acceptance_rate:
298
+ step_size *= adjustment_factor
299
+ else:
300
+ step_size /= adjustment_factor
301
+ acceptance_count = 0
302
+
303
+ temp *= sa_cooling_rate
304
+ step_size = max(step_size * sa_cooling_rate, 1e-8) # Maintain overall cooling trend for step size
305
+
306
+ return best_centers
307
+
308
+ def construct_packing(self):
309
+ """
310
+ Main method to construct the circle packing, orchestrating a multi-stage
311
+ optimization: initial placement, multi-res search, local SA, and global SA.
312
+ """
313
+ self._initial_grid_placement()
314
+
315
+ if self.n > 25:
316
+ # Stage 1: Multi-resolution search for the 26th circle's initial position
317
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
318
+
319
+ # Stage 2: Local refinement of the interstitial circle and its neighbors using SA
320
+ centers_after_local_sa, _ = self._local_refinement_cluster_sa(
321
+ centers_after_search,
322
+ sum_radii_after_search
323
+ )
324
+
325
+ # Stage 3: Global "gentle jiggle" refinement on all circles using SA
326
+ centers_after_global_sa = self._global_refinement_sa(centers_after_local_sa)
327
+ self.centers = centers_after_global_sa
328
+
329
+ # Final radius calculation for the fully optimized center configuration
330
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
331
+ return self.centers, self.radii
332
+
333
+
334
+ def construct_packing():
335
+ """
336
+ Constructs an arrangement of 26 circles by leveraging a superior three-stage
337
+ optimization strategy: initial grid, dense interstitial search, and localized SA.
338
+
339
+ Returns:
340
+ Tuple of (centers, radii)
341
+ centers: np.array of shape (26, 2) with (x, y) coordinates
342
+ radii: np.array of shape (26) with final radius of each circle
343
+ """
344
+ packer = CirclePacker(num_circles=26)
345
+ centers, radii = packer.construct_packing()
346
+ return centers, radii
347
+ # EVOLVE-BLOCK-END
348
+
349
+
350
+ # This part remains fixed (not evolved)
351
+ def run_packing():
352
+ """Run the circle packing constructor for n=26"""
353
+ centers, radii = construct_packing()
354
+ # Calculate the sum of radii
355
+ sum_radii = np.sum(radii)
356
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_100/original.py ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings within a unit square using a hybrid
9
+ optimization approach. It combines an exhaustive search for initial placement
10
+ with a localized refinement stage.
11
+ """
12
+ def __init__(self, num_circles=26):
13
+ self.n = num_circles
14
+ self.centers = np.zeros((self.n, 2))
15
+ self.radii = np.zeros(self.n)
16
+
17
+ @staticmethod
18
+ def _compute_max_radii_static(centers, n):
19
+ """
20
+ Computes the maximum possible radii for a given set of circle centers using
21
+ an iterative growth and constraint resolution method. This static version
22
+ incorporates an adaptive growth factor and dynamic tolerance for enhanced
23
+ performance and precision, based on the best prior implementation.
24
+
25
+ Args:
26
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
27
+ n (int): Number of circles.
28
+
29
+ Returns:
30
+ np.array: An array of shape (n) containing the final radius of each circle.
31
+ """
32
+ radii = np.zeros(n)
33
+
34
+ # Parameters for adaptive growth factor and dynamic tolerance
35
+ growth_factor_initial = 1.005
36
+ growth_factor_final = 1.002
37
+ tolerance_initial = 1e-7
38
+ tolerance_final = 1e-10
39
+
40
+ outer_iterations = 400
41
+ inner_iterations = 20
42
+
43
+ # Initialize radii based on the distance to the square's boundaries.
44
+ for i in range(n):
45
+ x, y = centers[i]
46
+ radii[i] = min(x, 1 - x, y, 1 - y)
47
+
48
+ for outer_iter_idx in range(outer_iterations):
49
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0 + 1e-9)
50
+ # Use exponential decay for smoother, more effective parameter transition, based on prior high-scoring versions.
51
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**interp_factor
52
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**interp_factor
53
+
54
+ radii *= current_growth_factor # Tentatively grow all radii
55
+
56
+ for _inner_iter_idx in range(inner_iterations):
57
+ constraints_changed = False
58
+
59
+ # Enforce boundary constraints with dynamic tolerance
60
+ for i in range(n):
61
+ x, y = centers[i]
62
+ boundary_limit = min(x, 1 - x, y, 1 - y)
63
+ if radii[i] > boundary_limit + current_tolerance:
64
+ radii[i] = boundary_limit
65
+ constraints_changed = True
66
+
67
+ # Resolve overlaps between circles with dynamic tolerance
68
+ for i in range(n):
69
+ for j in range(i + 1, n):
70
+ dist = np.linalg.norm(centers[i] - centers[j])
71
+ if radii[i] + radii[j] > dist + current_tolerance:
72
+ total_radius = radii[i] + radii[j]
73
+ if total_radius > tolerance_final:
74
+ scale = dist / total_radius
75
+ radii[i] *= scale
76
+ radii[j] *= scale
77
+ constraints_changed = True
78
+
79
+ if not constraints_changed:
80
+ break
81
+ return radii
82
+
83
+ def _initial_grid_placement(self):
84
+ """
85
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
86
+ """
87
+ coords = np.linspace(0.1, 0.9, 5)
88
+ grid_centers = np.array(list(product(coords, coords)))
89
+ self.centers[:25] = grid_centers
90
+
91
+ def _find_optimal_26th_circle_position(self):
92
+ """
93
+ Performs a multi-resolution grid search to find the best initial position
94
+ for the 26th circle among a dense set of interstitial candidates.
95
+ """
96
+ base_25_centers = np.copy(self.centers[:25])
97
+
98
+ # Initialize with a default position (center of the square) and calculate its sum of radii
99
+ optimal_26th_pos = np.array([0.5, 0.5])
100
+ trial_centers_initial = np.vstack([base_25_centers, optimal_26th_pos])
101
+ trial_radii_initial = CirclePacker._compute_max_radii_static(trial_centers_initial, self.n)
102
+ best_sum_radii = np.sum(trial_radii_initial)
103
+
104
+ # Keep track of the best position from the coarse search to center the fine search
105
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
106
+
107
+ interstitial_core_coords = np.linspace(0.2, 0.8, 4)
108
+
109
+ # --- Phase 1: Coarse Grid Search ---
110
+ # Explore a broader region first to identify promising areas.
111
+ coarse_delta = 0.05
112
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta])
113
+
114
+ coarse_candidate_points = []
115
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
116
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets):
117
+ coarse_candidate_points.append([base_x + offset_x, base_y + offset_y])
118
+
119
+ for candidate_pos in coarse_candidate_points:
120
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
121
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
122
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
123
+ current_sum_radii = np.sum(trial_radii)
124
+
125
+ if current_sum_radii > best_sum_radii:
126
+ best_sum_radii = current_sum_radii
127
+ optimal_26th_pos = clipped_candidate_pos
128
+ best_coarse_pos_for_fine_tuning = clipped_candidate_pos
129
+
130
+ # --- Phase 2: Fine Grid Search around the best coarse position ---
131
+ # Focus the search more precisely around the most promising area identified in Phase 1.
132
+ fine_delta = 0.01
133
+ fine_perturbation_offsets = np.array([-fine_delta, 0, fine_delta])
134
+
135
+ fine_candidate_points = []
136
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets):
137
+ fine_candidate_points.append([best_coarse_pos_for_fine_tuning[0] + offset_x, best_coarse_pos_for_fine_tuning[1] + offset_y])
138
+
139
+ for candidate_pos in fine_candidate_points:
140
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
141
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
142
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
143
+ current_sum_radii = np.sum(trial_radii)
144
+
145
+ if current_sum_radii > best_sum_radii:
146
+ best_sum_radii = current_sum_radii
147
+ optimal_26th_pos = clipped_candidate_pos
148
+
149
+ self.centers[25] = optimal_26th_pos
150
+
151
+ return self.centers, best_sum_radii
152
+
153
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
154
+ """
155
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
156
+ positions of a cluster of circles: the 26th and its nearest neighbors.
157
+ This allows the base grid to relax and better accommodate the interstitial circle.
158
+ """
159
+ current_centers = np.copy(initial_centers)
160
+ current_sum_radii = initial_sum_radii
161
+
162
+ best_centers_local = np.copy(current_centers)
163
+ best_sum_radii_local = current_sum_radii
164
+
165
+ # SA parameters adapted from high-performing prior implementations for fine-tuning
166
+ num_iterations = 150
167
+ initial_step_size = 0.005
168
+ initial_temp = 0.0001
169
+ cooling_rate = 0.99
170
+
171
+ step_size = initial_step_size
172
+ temp = initial_temp
173
+
174
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
175
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
176
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
177
+ cluster_indices = np.append(closest_neighbor_indices, 25)
178
+
179
+ for _ in range(num_iterations):
180
+ # Select a random circle from the cluster to perturb
181
+ idx_to_move = np.random.choice(cluster_indices)
182
+
183
+ trial_centers = np.copy(current_centers)
184
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
185
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
186
+
187
+ # Evaluate the new configuration
188
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
189
+ trial_sum_radii = np.sum(trial_radii)
190
+
191
+ # Metropolis-Hastings acceptance criterion
192
+ delta_energy = trial_sum_radii - current_sum_radii
193
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
194
+ current_centers = trial_centers
195
+ current_sum_radii = trial_sum_radii
196
+
197
+ if current_sum_radii > best_sum_radii_local:
198
+ best_sum_radii_local = current_sum_radii
199
+ best_centers_local = np.copy(current_centers)
200
+
201
+ temp *= cooling_rate
202
+ step_size = max(step_size * cooling_rate, 1e-7)
203
+
204
+ return best_centers_local, best_sum_radii_local
205
+
206
+ def _global_refinement_sa(self, initial_centers):
207
+ """
208
+ Applies a global, low-temperature Simulated Annealing search to fine-tune
209
+ the positions of ALL circles, acting as a "gentle jiggle" phase to
210
+ settle the entire packing into a better local optimum. This technique was
211
+ present in prior high-scoring implementations.
212
+ """
213
+ # SA parameters adapted from high-performing prior implementations for a thorough yet gentle search.
214
+ sa_iterations = 300
215
+ sa_initial_temp = 5e-6
216
+ sa_cooling_rate = 0.99
217
+ sa_initial_step_size = 0.001
218
+
219
+ current_centers = np.copy(initial_centers)
220
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
221
+ current_sum_radii = np.sum(current_radii)
222
+
223
+ best_centers = np.copy(current_centers)
224
+ best_sum_radii = current_sum_radii
225
+
226
+ temp = sa_initial_temp
227
+ step_size = sa_initial_step_size
228
+
229
+ all_indices = np.arange(self.n)
230
+
231
+ # Parameters for dynamic step size adjustment
232
+ acceptance_window = 30
233
+ acceptance_count = 0
234
+ target_acceptance_rate = 0.44 # Common target for SA
235
+ adjustment_factor = 1.05
236
+
237
+ for i in range(sa_iterations):
238
+ # Stress-based selection: prioritize moving circles with smaller radii.
239
+ inv_radii = 1.0 / (current_radii + 1e-9)
240
+ weights = (inv_radii - np.min(inv_radii))**2
241
+ if np.sum(weights) > 1e-9:
242
+ probabilities = weights / np.sum(weights)
243
+ idx_to_move = np.random.choice(all_indices, p=probabilities)
244
+ else:
245
+ idx_to_move = np.random.choice(all_indices)
246
+
247
+ trial_centers = np.copy(current_centers)
248
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
249
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
250
+
251
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
252
+ trial_sum_radii = np.sum(trial_radii)
253
+
254
+ delta_energy = trial_sum_radii - current_sum_radii
255
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
256
+ current_centers = trial_centers
257
+ current_radii = trial_radii # Keep radii in sync for stress calculation
258
+ current_sum_radii = trial_sum_radii
259
+ acceptance_count += 1
260
+
261
+ if current_sum_radii > best_sum_radii:
262
+ best_sum_radii = current_sum_radii
263
+ best_centers = np.copy(current_centers)
264
+
265
+ # Periodically adjust step_size based on acceptance rate to balance exploration/exploitation.
266
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
267
+ acceptance_rate = acceptance_count / acceptance_window
268
+ if acceptance_rate > target_acceptance_rate:
269
+ step_size *= adjustment_factor
270
+ else:
271
+ step_size /= adjustment_factor
272
+ acceptance_count = 0
273
+
274
+ temp *= sa_cooling_rate
275
+ step_size = max(step_size * sa_cooling_rate, 1e-8) # Maintain overall cooling trend for step size
276
+
277
+ return best_centers
278
+
279
+ def construct_packing(self):
280
+ """
281
+ Main method to construct the circle packing, orchestrating a multi-stage
282
+ optimization: initial placement, multi-res search, local SA, and global SA.
283
+ """
284
+ self._initial_grid_placement()
285
+
286
+ if self.n > 25:
287
+ # Stage 1: Multi-resolution search for the 26th circle's initial position
288
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
289
+
290
+ # Stage 2: Local refinement of the interstitial circle and its neighbors using SA
291
+ centers_after_local_sa, _ = self._local_refinement_cluster_sa(
292
+ centers_after_search,
293
+ sum_radii_after_search
294
+ )
295
+
296
+ # Stage 3: Global "gentle jiggle" refinement on all circles using SA
297
+ centers_after_global_sa = self._global_refinement_sa(centers_after_local_sa)
298
+ self.centers = centers_after_global_sa
299
+
300
+ # Final radius calculation for the fully optimized center configuration
301
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
302
+ return self.centers, self.radii
303
+
304
+
305
+ def construct_packing():
306
+ """
307
+ Constructs an arrangement of 26 circles by leveraging a superior three-stage
308
+ optimization strategy: initial grid, dense interstitial search, and localized SA.
309
+
310
+ Returns:
311
+ Tuple of (centers, radii)
312
+ centers: np.array of shape (26, 2) with (x, y) coordinates
313
+ radii: np.array of shape (26) with final radius of each circle
314
+ """
315
+ packer = CirclePacker(num_circles=26)
316
+ centers, radii = packer.construct_packing()
317
+ return centers, radii
318
+ # EVOLVE-BLOCK-END
319
+
320
+
321
+ # This part remains fixed (not evolved)
322
+ def run_packing():
323
+ """Run the circle packing constructor for n=26"""
324
+ centers, radii = construct_packing()
325
+ # Calculate the sum of radii
326
+ sum_radii = np.sum(radii)
327
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_100/search_replace.txt ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <NAME>
2
+ adaptive_local_sa_and_wider_initial_search
3
+ </NAME>
4
+
5
+ <DESCRIPTION>
6
+ This set of edits introduces two key improvements based on the actionable recommendations and analysis of high-performing strategies.
7
+
8
+ 1. **Expanded Initial Search Grid (`_find_optimal_26th_circle_position`)**: Following recommendation #1, the coarse search for the 26th circle's initial position has been significantly broadened. The grid of core interstitial points is expanded from 4x4 (`np.linspace(0.2, 0.8, 4)`) to 8x8 (`np.linspace(0.1, 0.9, 8)`). This increases the number of coarse candidate locations from 144 to 576, providing a much more thorough exploration of the search space. A better initial placement is crucial as it sets a higher baseline for all subsequent refinement stages, increasing the likelihood of finding a superior global optimum.
9
+
10
+ 2. **Enhanced Local Simulated Annealing (`_local_refinement_cluster_sa`)**: Following recommendations #3 and #4, the local SA stage is upgraded to incorporate techniques already proven effective in the global SA stage.
11
+ * **Stress-Based Perturbation Selection**: Instead of choosing a circle to move from the local cluster at random, the selection is now biased towards circles with smaller radii ("stressed" circles). This focuses computational effort on the most constrained parts of the local configuration, where adjustments are most likely to yield improvements.
12
+ * **Dynamic Step Size Adjustment**: The SA `step_size` is now dynamically adjusted based on the acceptance rate over a sliding window. This optimizes the exploration/exploitation trade-off, allowing the search to take larger steps when it's finding many improvements and smaller, more precise steps as it converges.
13
+
14
+ Together, these changes create a more powerful optimization pipeline: a more exhaustive initial search finds a better starting basin, and a more intelligent local refinement algorithm is better equipped to exploit it.
15
+ </DESCRIPTION>
16
+
17
+ <DIFF>
18
+ <<<<<<< SEARCH
19
+ # Keep track of the best position from the coarse search to center the fine search
20
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
21
+
22
+ interstitial_core_coords = np.linspace(0.2, 0.8, 4)
23
+
24
+ # --- Phase 1: Coarse Grid Search ---
25
+ =======
26
+ # Keep track of the best position from the coarse search to center the fine search
27
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
28
+
29
+ # Expand the coarse search grid to cover a broader area, as per recommendations.
30
+ interstitial_core_coords = np.linspace(0.1, 0.9, 8)
31
+
32
+ # --- Phase 1: Coarse Grid Search ---
33
+ >>>>>>> REPLACE
34
+ </DIFF>
35
+ <DIFF>
36
+ <<<<<<< SEARCH
37
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
38
+ """
39
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
40
+ positions of a cluster of circles: the 26th and its nearest neighbors.
41
+ This allows the base grid to relax and better accommodate the interstitial circle.
42
+ """
43
+ current_centers = np.copy(initial_centers)
44
+ current_sum_radii = initial_sum_radii
45
+
46
+ best_centers_local = np.copy(current_centers)
47
+ best_sum_radii_local = current_sum_radii
48
+
49
+ # SA parameters adapted from high-performing prior implementations for fine-tuning
50
+ num_iterations = 150
51
+ initial_step_size = 0.005
52
+ initial_temp = 0.0001
53
+ cooling_rate = 0.99
54
+
55
+ step_size = initial_step_size
56
+ temp = initial_temp
57
+
58
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
59
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
60
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
61
+ cluster_indices = np.append(closest_neighbor_indices, 25)
62
+
63
+ for _ in range(num_iterations):
64
+ # Select a random circle from the cluster to perturb
65
+ idx_to_move = np.random.choice(cluster_indices)
66
+
67
+ trial_centers = np.copy(current_centers)
68
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
69
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
70
+
71
+ # Evaluate the new configuration
72
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
73
+ trial_sum_radii = np.sum(trial_radii)
74
+
75
+ # Metropolis-Hastings acceptance criterion
76
+ delta_energy = trial_sum_radii - current_sum_radii
77
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
78
+ current_centers = trial_centers
79
+ current_sum_radii = trial_sum_radii
80
+
81
+ if current_sum_radii > best_sum_radii_local:
82
+ best_sum_radii_local = current_sum_radii
83
+ best_centers_local = np.copy(current_centers)
84
+
85
+ temp *= cooling_rate
86
+ step_size = max(step_size * cooling_rate, 1e-7)
87
+
88
+ return best_centers_local, best_sum_radii_local
89
+ =======
90
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
91
+ """
92
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
93
+ positions of a cluster of circles: the 26th and its nearest neighbors.
94
+ This allows the base grid to relax and better accommodate the interstitial circle.
95
+ This version is enhanced with stress-based selection and dynamic step size adjustment.
96
+ """
97
+ current_centers = np.copy(initial_centers)
98
+ # Compute radii for initial stress calculation and consistent sum.
99
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
100
+ current_sum_radii = np.sum(current_radii)
101
+
102
+ best_centers_local = np.copy(current_centers)
103
+ best_sum_radii_local = current_sum_radii
104
+
105
+ # SA parameters adapted from high-performing prior implementations for fine-tuning
106
+ num_iterations = 150
107
+ initial_step_size = 0.005
108
+ initial_temp = 0.0001
109
+ cooling_rate = 0.99
110
+
111
+ step_size = initial_step_size
112
+ temp = initial_temp
113
+
114
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
115
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
116
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
117
+ cluster_indices = np.append(closest_neighbor_indices, 25)
118
+
119
+ # Parameters for dynamic step size adjustment
120
+ acceptance_window = 30
121
+ acceptance_count = 0
122
+ target_acceptance_rate = 0.44
123
+ adjustment_factor = 1.05
124
+
125
+ for i in range(num_iterations):
126
+ # Stress-based selection within the cluster
127
+ cluster_radii = current_radii[cluster_indices]
128
+ inv_radii = 1.0 / (cluster_radii + 1e-9)
129
+ weights = (inv_radii - np.min(inv_radii))**2
130
+ if np.sum(weights) > 1e-9:
131
+ probabilities = weights / np.sum(weights)
132
+ idx_to_move = np.random.choice(cluster_indices, p=probabilities)
133
+ else:
134
+ idx_to_move = np.random.choice(cluster_indices)
135
+
136
+
137
+ trial_centers = np.copy(current_centers)
138
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
139
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
140
+
141
+ # Evaluate the new configuration
142
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
143
+ trial_sum_radii = np.sum(trial_radii)
144
+
145
+ # Metropolis-Hastings acceptance criterion
146
+ delta_energy = trial_sum_radii - current_sum_radii
147
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
148
+ current_centers = trial_centers
149
+ current_radii = trial_radii # Update radii for stress calculation
150
+ current_sum_radii = trial_sum_radii
151
+ acceptance_count += 1
152
+
153
+ if current_sum_radii > best_sum_radii_local:
154
+ best_sum_radii_local = current_sum_radii
155
+ best_centers_local = np.copy(current_centers)
156
+
157
+ # Dynamic step size adjustment
158
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
159
+ acceptance_rate = acceptance_count / acceptance_window
160
+ if acceptance_rate > target_acceptance_rate:
161
+ step_size *= adjustment_factor
162
+ else:
163
+ step_size /= adjustment_factor
164
+ acceptance_count = 0
165
+
166
+ temp *= cooling_rate
167
+ step_size = max(step_size * cooling_rate, 1e-7)
168
+
169
+ return best_centers_local, best_sum_radii_local
170
+ >>>>>>> REPLACE
171
+ </DIFF>
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_101/edit.diff ADDED
@@ -0,0 +1,300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,237 +1,284 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+ from itertools import product
7
+
8
+
9
+ class CirclePacker:
10
+ """
11
+ A class to construct circle packings within a unit square.
12
+ It encapsulates the logic for initial placement and iterative radius adjustment.
13
+ """
14
+ def __init__(self, num_circles=26):
15
+ self.n = num_circles
16
+ self.centers = np.zeros((self.n, 2))
17
+ self.radii = np.zeros(self.n)
18
+
19
+ @staticmethod
20
+ def _compute_max_radii_static(centers, n):
21
+ """
22
+ Computes the maximum possible radii for a given set of circle centers using
23
+ an iterative growth and constraint resolution method. This static version
24
+ incorporates adaptive growth factor and dynamic tolerance for enhanced
25
+ performance and precision.
26
+
27
+ Args:
28
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
29
+ n (int): Number of circles.
30
+
31
+ Returns:
32
+ np.array: An array of shape (n) containing the final radius of each circle.
33
+ """
34
+ radii = np.zeros(n)
35
+
36
+ # Parameters for adaptive growth factor (Recommendation 1 from previous feedback)
37
+ growth_factor_initial = 1.005 # Start with a higher growth pressure
38
+ growth_factor_final = 1.002 # Gradually decrease to the original value
39
+
40
+ # Parameters for dynamic tolerance (Recommendation 5 from previous feedback)
41
+ tolerance_initial = 1e-7 # Start with a looser tolerance to quickly resolve major overlaps
42
+ tolerance_final = 1e-10 # End with a tighter tolerance for precision
43
+
44
+ outer_iterations = 400
45
+ inner_iterations = 20 # Using 20 inner iterations as in the high-scoring program
46
+
47
+ # Initialize radii based on the distance to the square's boundaries.
48
+ for i in range(n):
49
+ x, y = centers[i]
50
+ radii[i] = min(x, 1 - x, y, 1 - y)
51
+
52
+ for outer_iter_idx in range(outer_iterations):
53
+ - # Calculate dynamic growth factor (linear decay)
54
+ - # Factor goes from 0 (at first iter) to 1 (at last iter)
55
+ - interp_factor = outer_iter_idx / (outer_iterations - 1.0)
56
+ - current_growth_factor = growth_factor_initial - interp_factor * \
57
+ - (growth_factor_initial - growth_factor_final)
58
+ -
59
+ - # Calculate dynamic tolerance (linear decay)
60
+ - current_tolerance = tolerance_initial - interp_factor * \
61
+ - (tolerance_initial - tolerance_final)
62
+ + # Use exponential interpolation for smoother parameter transition
63
+ + progress = outer_iter_idx / (outer_iterations - 1 + 1e-9)
64
+ + current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**progress
65
+ + current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**progress
66
+
67
+ radii *= current_growth_factor # Tentatively grow all radii
68
+
69
+ for _inner_iter_idx in range(inner_iterations):
70
+ constraints_changed = False
71
+
72
+ # Enforce boundary constraints with dynamic tolerance
73
+ for i in range(n):
74
+ x, y = centers[i]
75
+ boundary_limit = min(x, 1 - x, y, 1 - y)
76
+ if radii[i] > boundary_limit + current_tolerance:
77
+ radii[i] = boundary_limit
78
+ constraints_changed = True
79
+
80
+ # Resolve overlaps between circles with dynamic tolerance
81
+ for i in range(n):
82
+ for j in range(i + 1, n):
83
+ # Use np.linalg.norm for cleaner distance calculation
84
+ dist = np.linalg.norm(centers[i] - centers[j])
85
+ if radii[i] + radii[j] > dist + current_tolerance:
86
+ total_radius = radii[i] + radii[j]
87
+ # Use final_tolerance for the small radius check to avoid division by zero
88
+ if total_radius > tolerance_final:
89
+ scale = dist / total_radius
90
+ radii[i] *= scale
91
+ radii[j] *= scale
92
+ constraints_changed = True
93
+
94
+ if not constraints_changed:
95
+ # Inner loop converged, all constraints resolved for this growth step
96
+ break
97
+ return radii
98
+
99
+ def _initial_grid_placement(self):
100
+ """
101
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
102
+ """
103
+ coords = np.linspace(0.1, 0.9, 5)
104
+ grid_centers = np.array(list(product(coords, coords)))
105
+ self.centers[:25] = grid_centers
106
+
107
+ def _find_optimal_26th_circle_position(self):
108
+ """
109
+ Performs an exhaustive search to find the best initial position for the
110
+ 26th circle among a set of interstitial candidates. This method runs
111
+ the full radius optimization for each candidate.
112
+ """
113
+ base_25_centers = np.copy(self.centers[:25])
114
+
115
+ # Candidate locations for the 26th circle (16 interstitial voids, as in the best prior program)
116
+ interstitial_coords = np.linspace(0.2, 0.8, 4)
117
+ candidate_points = list(product(interstitial_coords, interstitial_coords))
118
+
119
+ best_sum_radii = -1.0
120
+ optimal_26th_pos = None
121
+
122
+ for candidate_pos in candidate_points:
123
+ trial_centers = np.vstack([base_25_centers, candidate_pos])
124
+ # Evaluate using the static compute_max_radii function
125
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
126
+ current_sum_radii = np.sum(trial_radii)
127
+
128
+ if current_sum_radii > best_sum_radii:
129
+ best_sum_radii = current_sum_radii
130
+ optimal_26th_pos = np.array(candidate_pos)
131
+
132
+ if optimal_26th_pos is not None:
133
+ self.centers[25] = optimal_26th_pos
134
+ else:
135
+ # Fallback: Should not be reached if candidate_points is non-empty
136
+ self.centers[25] = [0.5, 0.5]
137
+
138
+ return self.centers, best_sum_radii
139
+
140
+ def _local_refinement_26th_circle(self, initial_centers, initial_sum_radii, index_to_perturb=25):
141
+ """
142
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
143
+ position of a single specified circle (the 26th circle in this case),
144
+ starting from an already good initial placement. (Recommendation 4 from previous feedback)
145
+ """
146
+
147
+ current_centers = np.copy(initial_centers)
148
+ current_sum_radii = initial_sum_radii
149
+
150
+ best_centers_local = np.copy(current_centers)
151
+ best_sum_radii_local = current_sum_radii
152
+
153
+ # SA parameters for a very short, localized search
154
+ num_iterations = 200 # Small number of iterations for targeted refinement
155
+ initial_step_size = 0.01 # Very small step size for local search
156
+ initial_temp = 0.01 # Low initial temperature for fast convergence
157
+ cooling_rate = 0.98 # Fast cooling rate
158
+
159
+ for k in range(num_iterations):
160
+ # Step size decreases linearly
161
+ progress = k / num_iterations
162
+ step_size = initial_step_size * (1.0 - progress)
163
+ # Temperature decreases exponentially
164
+ temp = initial_temp * (cooling_rate**k)
165
+
166
+ # Perturb ONLY the specified circle's center
167
+ trial_centers = np.copy(current_centers)
168
+ random_angle = np.random.uniform(0, 2 * np.pi)
169
+ dx = step_size * np.cos(random_angle)
170
+ dy = step_size * np.sin(random_angle)
171
+
172
+ trial_centers[index_to_perturb, 0] += dx
173
+ trial_centers[index_to_perturb, 1] += dy
174
+
175
+ # Ensure the new center is within the unit square [0, 1]
176
+ trial_centers[index_to_perturb] = np.clip(trial_centers[index_to_perturb], 0.0, 1.0)
177
+
178
+ # Evaluate the new configuration using the static compute_max_radii
179
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
180
+ trial_sum_radii = np.sum(trial_radii)
181
+
182
+ # Metropolis-Hastings acceptance criterion for maximization
183
+ delta_energy = trial_sum_radii - current_sum_radii
184
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
185
+ current_centers = trial_centers
186
+ current_sum_radii = trial_sum_radii
187
+
188
+ if current_sum_radii > best_sum_radii_local:
189
+ best_sum_radii_local = current_sum_radii
190
+ best_centers_local = np.copy(current_centers)
191
+
192
+ return best_centers_local, best_sum_radii_local
193
+
194
+ + def _global_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
195
+ + """
196
+ + Applies a global, low-temperature SA to "jiggle" all circles into a
197
+ + better global optimum, using tuned parameters from high-scoring versions.
198
+ + """
199
+ + current_centers = np.copy(initial_centers)
200
+ + current_sum_radii = initial_sum_radii
201
+ +
202
+ + best_centers_global = np.copy(current_centers)
203
+ + best_sum_radii_global = current_sum_radii
204
+ +
205
+ + # SA parameters for a final, gentle, global refinement
206
+ + num_iterations = 1500
207
+ + initial_step_size = 0.004
208
+ + initial_temp = 1e-5
209
+ + cooling_rate = 0.995
210
+ +
211
+ + step_size = initial_step_size
212
+ + temp = initial_temp
213
+ +
214
+ + for _ in range(num_iterations):
215
+ + # Perturb a random circle from the entire set
216
+ + idx_to_perturb = np.random.randint(self.n)
217
+ +
218
+ + trial_centers = np.copy(current_centers)
219
+ + move = (np.random.rand(2) - 0.5) * 2 * step_size
220
+ + trial_centers[idx_to_perturb] += move
221
+ + trial_centers[idx_to_perturb] = np.clip(trial_centers[idx_to_perturb], 0.0, 1.0)
222
+ +
223
+ + trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
224
+ + trial_sum_radii = np.sum(trial_radii)
225
+ +
226
+ + delta_energy = trial_sum_radii - current_sum_radii
227
+ + if delta_energy > 0 or (temp > 1e-9 and np.random.random() < np.exp(delta_energy / temp)):
228
+ + current_centers = trial_centers
229
+ + current_sum_radii = trial_sum_radii
230
+ +
231
+ + if current_sum_radii > best_sum_radii_global:
232
+ + best_sum_radii_global = current_sum_radii
233
+ + best_centers_global = np.copy(current_centers)
234
+ +
235
+ + temp *= cooling_rate
236
+ + step_size = max(step_size * cooling_rate, 5e-8)
237
+ +
238
+ + return best_centers_global, best_sum_radii_global
239
+ +
240
+ def construct_packing(self):
241
+ """
242
+ Main method to construct the circle packing.
243
+ Orchestrates initial placement, exhaustive search, and local refinement.
244
+ """
245
+ self._initial_grid_placement()
246
+
247
+ if self.n > 25:
248
+ # Stage 1: Exhaustive search for the optimal 26th circle position
249
+ # This identifies a strong starting point for the 26th circle.
250
+ initial_centers_for_refinement, sum_radii_after_exhaustive = self._find_optimal_26th_circle_position()
251
+
252
+ # Stage 2: Local refinement using SA for the 26th circle's position
253
+ # This fine-tunes the position to potentially eke out further gains.
254
+ - refined_centers, _ = self._local_refinement_26th_circle(
255
+ + centers_after_local_refinement, sum_radii_after_local_refinement = self._local_refinement_26th_circle(
256
+ initial_centers_for_refinement,
257
+ sum_radii_after_exhaustive,
258
+ index_to_perturb=25
259
+ )
260
+ - self.centers = refined_centers # Update the instance's centers with the refined ones
261
+ -
262
+ - # Final radius calculation for the optimized center configuration
263
+ +
264
+ + # Stage 3: Global "gentle jiggle" refinement using SA on all circles.
265
+ + refined_centers, _ = self._global_refinement_sa(
266
+ + centers_after_local_refinement,
267
+ + sum_radii_after_local_refinement
268
+ + )
269
+ + self.centers = refined_centers # Update the instance's centers with the globally refined ones
270
+ +
271
+ + # Final radius calculation for the fully optimized center configuration
272
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
273
+ return self.centers, self.radii
274
+
275
+
276
+ def construct_packing():
277
+ """
278
+ Constructs an arrangement of 26 circles by leveraging a modular
279
+ object-oriented approach. It initializes with a 5x5 grid and
280
+ dynamically places the 26th circle in the most optimal interstitial
281
+ void, then iteratively adjusts radii to maximize their sum.
282
+
283
+ Returns:
284
+ Tuple of (centers, radii)
285
+ centers: np.array of shape (26, 2) with (x, y) coordinates
286
+ radii: np.array of shape (26) with final radius of each circle
287
+ """
288
+ packer = CirclePacker(num_circles=26)
289
+ centers, radii = packer.construct_packing()
290
+ return centers, radii
291
+ # EVOLVE-BLOCK-END
292
+
293
+
294
+ # This part remains fixed (not evolved)
295
+ def run_packing():
296
+ """Run the circle packing constructor for n=26"""
297
+ centers, radii = construct_packing()
298
+ # Calculate the sum of radii
299
+ sum_radii = np.sum(radii)
300
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_101/main.py ADDED
@@ -0,0 +1,284 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings within a unit square.
9
+ It encapsulates the logic for initial placement and iterative radius adjustment.
10
+ """
11
+ def __init__(self, num_circles=26):
12
+ self.n = num_circles
13
+ self.centers = np.zeros((self.n, 2))
14
+ self.radii = np.zeros(self.n)
15
+
16
+ @staticmethod
17
+ def _compute_max_radii_static(centers, n):
18
+ """
19
+ Computes the maximum possible radii for a given set of circle centers using
20
+ an iterative growth and constraint resolution method. This static version
21
+ incorporates adaptive growth factor and dynamic tolerance for enhanced
22
+ performance and precision.
23
+
24
+ Args:
25
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
26
+ n (int): Number of circles.
27
+
28
+ Returns:
29
+ np.array: An array of shape (n) containing the final radius of each circle.
30
+ """
31
+ radii = np.zeros(n)
32
+
33
+ # Parameters for adaptive growth factor (Recommendation 1 from previous feedback)
34
+ growth_factor_initial = 1.005 # Start with a higher growth pressure
35
+ growth_factor_final = 1.002 # Gradually decrease to the original value
36
+
37
+ # Parameters for dynamic tolerance (Recommendation 5 from previous feedback)
38
+ tolerance_initial = 1e-7 # Start with a looser tolerance to quickly resolve major overlaps
39
+ tolerance_final = 1e-10 # End with a tighter tolerance for precision
40
+
41
+ outer_iterations = 400
42
+ inner_iterations = 20 # Using 20 inner iterations as in the high-scoring program
43
+
44
+ # Initialize radii based on the distance to the square's boundaries.
45
+ for i in range(n):
46
+ x, y = centers[i]
47
+ radii[i] = min(x, 1 - x, y, 1 - y)
48
+
49
+ for outer_iter_idx in range(outer_iterations):
50
+ # Use exponential interpolation for smoother parameter transition
51
+ progress = outer_iter_idx / (outer_iterations - 1 + 1e-9)
52
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**progress
53
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**progress
54
+
55
+ radii *= current_growth_factor # Tentatively grow all radii
56
+
57
+ for _inner_iter_idx in range(inner_iterations):
58
+ constraints_changed = False
59
+
60
+ # Enforce boundary constraints with dynamic tolerance
61
+ for i in range(n):
62
+ x, y = centers[i]
63
+ boundary_limit = min(x, 1 - x, y, 1 - y)
64
+ if radii[i] > boundary_limit + current_tolerance:
65
+ radii[i] = boundary_limit
66
+ constraints_changed = True
67
+
68
+ # Resolve overlaps between circles with dynamic tolerance
69
+ for i in range(n):
70
+ for j in range(i + 1, n):
71
+ # Use np.linalg.norm for cleaner distance calculation
72
+ dist = np.linalg.norm(centers[i] - centers[j])
73
+ if radii[i] + radii[j] > dist + current_tolerance:
74
+ total_radius = radii[i] + radii[j]
75
+ # Use final_tolerance for the small radius check to avoid division by zero
76
+ if total_radius > tolerance_final:
77
+ scale = dist / total_radius
78
+ radii[i] *= scale
79
+ radii[j] *= scale
80
+ constraints_changed = True
81
+
82
+ if not constraints_changed:
83
+ # Inner loop converged, all constraints resolved for this growth step
84
+ break
85
+ return radii
86
+
87
+ def _initial_grid_placement(self):
88
+ """
89
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
90
+ """
91
+ coords = np.linspace(0.1, 0.9, 5)
92
+ grid_centers = np.array(list(product(coords, coords)))
93
+ self.centers[:25] = grid_centers
94
+
95
+ def _find_optimal_26th_circle_position(self):
96
+ """
97
+ Performs an exhaustive search to find the best initial position for the
98
+ 26th circle among a set of interstitial candidates. This method runs
99
+ the full radius optimization for each candidate.
100
+ """
101
+ base_25_centers = np.copy(self.centers[:25])
102
+
103
+ # Candidate locations for the 26th circle (16 interstitial voids, as in the best prior program)
104
+ interstitial_coords = np.linspace(0.2, 0.8, 4)
105
+ candidate_points = list(product(interstitial_coords, interstitial_coords))
106
+
107
+ best_sum_radii = -1.0
108
+ optimal_26th_pos = None
109
+
110
+ for candidate_pos in candidate_points:
111
+ trial_centers = np.vstack([base_25_centers, candidate_pos])
112
+ # Evaluate using the static compute_max_radii function
113
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
114
+ current_sum_radii = np.sum(trial_radii)
115
+
116
+ if current_sum_radii > best_sum_radii:
117
+ best_sum_radii = current_sum_radii
118
+ optimal_26th_pos = np.array(candidate_pos)
119
+
120
+ if optimal_26th_pos is not None:
121
+ self.centers[25] = optimal_26th_pos
122
+ else:
123
+ # Fallback: Should not be reached if candidate_points is non-empty
124
+ self.centers[25] = [0.5, 0.5]
125
+
126
+ return self.centers, best_sum_radii
127
+
128
+ def _local_refinement_26th_circle(self, initial_centers, initial_sum_radii, index_to_perturb=25):
129
+ """
130
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
131
+ position of a single specified circle (the 26th circle in this case),
132
+ starting from an already good initial placement. (Recommendation 4 from previous feedback)
133
+ """
134
+
135
+ current_centers = np.copy(initial_centers)
136
+ current_sum_radii = initial_sum_radii
137
+
138
+ best_centers_local = np.copy(current_centers)
139
+ best_sum_radii_local = current_sum_radii
140
+
141
+ # SA parameters for a very short, localized search
142
+ num_iterations = 200 # Small number of iterations for targeted refinement
143
+ initial_step_size = 0.01 # Very small step size for local search
144
+ initial_temp = 0.01 # Low initial temperature for fast convergence
145
+ cooling_rate = 0.98 # Fast cooling rate
146
+
147
+ for k in range(num_iterations):
148
+ # Step size decreases linearly
149
+ progress = k / num_iterations
150
+ step_size = initial_step_size * (1.0 - progress)
151
+ # Temperature decreases exponentially
152
+ temp = initial_temp * (cooling_rate**k)
153
+
154
+ # Perturb ONLY the specified circle's center
155
+ trial_centers = np.copy(current_centers)
156
+ random_angle = np.random.uniform(0, 2 * np.pi)
157
+ dx = step_size * np.cos(random_angle)
158
+ dy = step_size * np.sin(random_angle)
159
+
160
+ trial_centers[index_to_perturb, 0] += dx
161
+ trial_centers[index_to_perturb, 1] += dy
162
+
163
+ # Ensure the new center is within the unit square [0, 1]
164
+ trial_centers[index_to_perturb] = np.clip(trial_centers[index_to_perturb], 0.0, 1.0)
165
+
166
+ # Evaluate the new configuration using the static compute_max_radii
167
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
168
+ trial_sum_radii = np.sum(trial_radii)
169
+
170
+ # Metropolis-Hastings acceptance criterion for maximization
171
+ delta_energy = trial_sum_radii - current_sum_radii
172
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
173
+ current_centers = trial_centers
174
+ current_sum_radii = trial_sum_radii
175
+
176
+ if current_sum_radii > best_sum_radii_local:
177
+ best_sum_radii_local = current_sum_radii
178
+ best_centers_local = np.copy(current_centers)
179
+
180
+ return best_centers_local, best_sum_radii_local
181
+
182
+ def _global_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
183
+ """
184
+ Applies a global, low-temperature SA to "jiggle" all circles into a
185
+ better global optimum, using tuned parameters from high-scoring versions.
186
+ """
187
+ current_centers = np.copy(initial_centers)
188
+ current_sum_radii = initial_sum_radii
189
+
190
+ best_centers_global = np.copy(current_centers)
191
+ best_sum_radii_global = current_sum_radii
192
+
193
+ # SA parameters for a final, gentle, global refinement
194
+ num_iterations = 1500
195
+ initial_step_size = 0.004
196
+ initial_temp = 1e-5
197
+ cooling_rate = 0.995
198
+
199
+ step_size = initial_step_size
200
+ temp = initial_temp
201
+
202
+ for _ in range(num_iterations):
203
+ # Perturb a random circle from the entire set
204
+ idx_to_perturb = np.random.randint(self.n)
205
+
206
+ trial_centers = np.copy(current_centers)
207
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
208
+ trial_centers[idx_to_perturb] += move
209
+ trial_centers[idx_to_perturb] = np.clip(trial_centers[idx_to_perturb], 0.0, 1.0)
210
+
211
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
212
+ trial_sum_radii = np.sum(trial_radii)
213
+
214
+ delta_energy = trial_sum_radii - current_sum_radii
215
+ if delta_energy > 0 or (temp > 1e-9 and np.random.random() < np.exp(delta_energy / temp)):
216
+ current_centers = trial_centers
217
+ current_sum_radii = trial_sum_radii
218
+
219
+ if current_sum_radii > best_sum_radii_global:
220
+ best_sum_radii_global = current_sum_radii
221
+ best_centers_global = np.copy(current_centers)
222
+
223
+ temp *= cooling_rate
224
+ step_size = max(step_size * cooling_rate, 5e-8)
225
+
226
+ return best_centers_global, best_sum_radii_global
227
+
228
+ def construct_packing(self):
229
+ """
230
+ Main method to construct the circle packing.
231
+ Orchestrates initial placement, exhaustive search, and local refinement.
232
+ """
233
+ self._initial_grid_placement()
234
+
235
+ if self.n > 25:
236
+ # Stage 1: Exhaustive search for the optimal 26th circle position
237
+ # This identifies a strong starting point for the 26th circle.
238
+ initial_centers_for_refinement, sum_radii_after_exhaustive = self._find_optimal_26th_circle_position()
239
+
240
+ # Stage 2: Local refinement using SA for the 26th circle's position
241
+ # This fine-tunes the position to potentially eke out further gains.
242
+ centers_after_local_refinement, sum_radii_after_local_refinement = self._local_refinement_26th_circle(
243
+ initial_centers_for_refinement,
244
+ sum_radii_after_exhaustive,
245
+ index_to_perturb=25
246
+ )
247
+
248
+ # Stage 3: Global "gentle jiggle" refinement using SA on all circles.
249
+ refined_centers, _ = self._global_refinement_sa(
250
+ centers_after_local_refinement,
251
+ sum_radii_after_local_refinement
252
+ )
253
+ self.centers = refined_centers # Update the instance's centers with the globally refined ones
254
+
255
+ # Final radius calculation for the fully optimized center configuration
256
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
257
+ return self.centers, self.radii
258
+
259
+
260
+ def construct_packing():
261
+ """
262
+ Constructs an arrangement of 26 circles by leveraging a modular
263
+ object-oriented approach. It initializes with a 5x5 grid and
264
+ dynamically places the 26th circle in the most optimal interstitial
265
+ void, then iteratively adjusts radii to maximize their sum.
266
+
267
+ Returns:
268
+ Tuple of (centers, radii)
269
+ centers: np.array of shape (26, 2) with (x, y) coordinates
270
+ radii: np.array of shape (26) with final radius of each circle
271
+ """
272
+ packer = CirclePacker(num_circles=26)
273
+ centers, radii = packer.construct_packing()
274
+ return centers, radii
275
+ # EVOLVE-BLOCK-END
276
+
277
+
278
+ # This part remains fixed (not evolved)
279
+ def run_packing():
280
+ """Run the circle packing constructor for n=26"""
281
+ centers, radii = construct_packing()
282
+ # Calculate the sum of radii
283
+ sum_radii = np.sum(radii)
284
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_101/original.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings within a unit square.
9
+ It encapsulates the logic for initial placement and iterative radius adjustment.
10
+ """
11
+ def __init__(self, num_circles=26):
12
+ self.n = num_circles
13
+ self.centers = np.zeros((self.n, 2))
14
+ self.radii = np.zeros(self.n)
15
+
16
+ @staticmethod
17
+ def _compute_max_radii_static(centers, n):
18
+ """
19
+ Computes the maximum possible radii for a given set of circle centers using
20
+ an iterative growth and constraint resolution method. This static version
21
+ incorporates adaptive growth factor and dynamic tolerance for enhanced
22
+ performance and precision.
23
+
24
+ Args:
25
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
26
+ n (int): Number of circles.
27
+
28
+ Returns:
29
+ np.array: An array of shape (n) containing the final radius of each circle.
30
+ """
31
+ radii = np.zeros(n)
32
+
33
+ # Parameters for adaptive growth factor (Recommendation 1 from previous feedback)
34
+ growth_factor_initial = 1.005 # Start with a higher growth pressure
35
+ growth_factor_final = 1.002 # Gradually decrease to the original value
36
+
37
+ # Parameters for dynamic tolerance (Recommendation 5 from previous feedback)
38
+ tolerance_initial = 1e-7 # Start with a looser tolerance to quickly resolve major overlaps
39
+ tolerance_final = 1e-10 # End with a tighter tolerance for precision
40
+
41
+ outer_iterations = 400
42
+ inner_iterations = 20 # Using 20 inner iterations as in the high-scoring program
43
+
44
+ # Initialize radii based on the distance to the square's boundaries.
45
+ for i in range(n):
46
+ x, y = centers[i]
47
+ radii[i] = min(x, 1 - x, y, 1 - y)
48
+
49
+ for outer_iter_idx in range(outer_iterations):
50
+ # Calculate dynamic growth factor (linear decay)
51
+ # Factor goes from 0 (at first iter) to 1 (at last iter)
52
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0)
53
+ current_growth_factor = growth_factor_initial - interp_factor * \
54
+ (growth_factor_initial - growth_factor_final)
55
+
56
+ # Calculate dynamic tolerance (linear decay)
57
+ current_tolerance = tolerance_initial - interp_factor * \
58
+ (tolerance_initial - tolerance_final)
59
+
60
+ radii *= current_growth_factor # Tentatively grow all radii
61
+
62
+ for _inner_iter_idx in range(inner_iterations):
63
+ constraints_changed = False
64
+
65
+ # Enforce boundary constraints with dynamic tolerance
66
+ for i in range(n):
67
+ x, y = centers[i]
68
+ boundary_limit = min(x, 1 - x, y, 1 - y)
69
+ if radii[i] > boundary_limit + current_tolerance:
70
+ radii[i] = boundary_limit
71
+ constraints_changed = True
72
+
73
+ # Resolve overlaps between circles with dynamic tolerance
74
+ for i in range(n):
75
+ for j in range(i + 1, n):
76
+ # Use np.linalg.norm for cleaner distance calculation
77
+ dist = np.linalg.norm(centers[i] - centers[j])
78
+ if radii[i] + radii[j] > dist + current_tolerance:
79
+ total_radius = radii[i] + radii[j]
80
+ # Use final_tolerance for the small radius check to avoid division by zero
81
+ if total_radius > tolerance_final:
82
+ scale = dist / total_radius
83
+ radii[i] *= scale
84
+ radii[j] *= scale
85
+ constraints_changed = True
86
+
87
+ if not constraints_changed:
88
+ # Inner loop converged, all constraints resolved for this growth step
89
+ break
90
+ return radii
91
+
92
+ def _initial_grid_placement(self):
93
+ """
94
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
95
+ """
96
+ coords = np.linspace(0.1, 0.9, 5)
97
+ grid_centers = np.array(list(product(coords, coords)))
98
+ self.centers[:25] = grid_centers
99
+
100
+ def _find_optimal_26th_circle_position(self):
101
+ """
102
+ Performs an exhaustive search to find the best initial position for the
103
+ 26th circle among a set of interstitial candidates. This method runs
104
+ the full radius optimization for each candidate.
105
+ """
106
+ base_25_centers = np.copy(self.centers[:25])
107
+
108
+ # Candidate locations for the 26th circle (16 interstitial voids, as in the best prior program)
109
+ interstitial_coords = np.linspace(0.2, 0.8, 4)
110
+ candidate_points = list(product(interstitial_coords, interstitial_coords))
111
+
112
+ best_sum_radii = -1.0
113
+ optimal_26th_pos = None
114
+
115
+ for candidate_pos in candidate_points:
116
+ trial_centers = np.vstack([base_25_centers, candidate_pos])
117
+ # Evaluate using the static compute_max_radii function
118
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
119
+ current_sum_radii = np.sum(trial_radii)
120
+
121
+ if current_sum_radii > best_sum_radii:
122
+ best_sum_radii = current_sum_radii
123
+ optimal_26th_pos = np.array(candidate_pos)
124
+
125
+ if optimal_26th_pos is not None:
126
+ self.centers[25] = optimal_26th_pos
127
+ else:
128
+ # Fallback: Should not be reached if candidate_points is non-empty
129
+ self.centers[25] = [0.5, 0.5]
130
+
131
+ return self.centers, best_sum_radii
132
+
133
+ def _local_refinement_26th_circle(self, initial_centers, initial_sum_radii, index_to_perturb=25):
134
+ """
135
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
136
+ position of a single specified circle (the 26th circle in this case),
137
+ starting from an already good initial placement. (Recommendation 4 from previous feedback)
138
+ """
139
+
140
+ current_centers = np.copy(initial_centers)
141
+ current_sum_radii = initial_sum_radii
142
+
143
+ best_centers_local = np.copy(current_centers)
144
+ best_sum_radii_local = current_sum_radii
145
+
146
+ # SA parameters for a very short, localized search
147
+ num_iterations = 200 # Small number of iterations for targeted refinement
148
+ initial_step_size = 0.01 # Very small step size for local search
149
+ initial_temp = 0.01 # Low initial temperature for fast convergence
150
+ cooling_rate = 0.98 # Fast cooling rate
151
+
152
+ for k in range(num_iterations):
153
+ # Step size decreases linearly
154
+ progress = k / num_iterations
155
+ step_size = initial_step_size * (1.0 - progress)
156
+ # Temperature decreases exponentially
157
+ temp = initial_temp * (cooling_rate**k)
158
+
159
+ # Perturb ONLY the specified circle's center
160
+ trial_centers = np.copy(current_centers)
161
+ random_angle = np.random.uniform(0, 2 * np.pi)
162
+ dx = step_size * np.cos(random_angle)
163
+ dy = step_size * np.sin(random_angle)
164
+
165
+ trial_centers[index_to_perturb, 0] += dx
166
+ trial_centers[index_to_perturb, 1] += dy
167
+
168
+ # Ensure the new center is within the unit square [0, 1]
169
+ trial_centers[index_to_perturb] = np.clip(trial_centers[index_to_perturb], 0.0, 1.0)
170
+
171
+ # Evaluate the new configuration using the static compute_max_radii
172
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
173
+ trial_sum_radii = np.sum(trial_radii)
174
+
175
+ # Metropolis-Hastings acceptance criterion for maximization
176
+ delta_energy = trial_sum_radii - current_sum_radii
177
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
178
+ current_centers = trial_centers
179
+ current_sum_radii = trial_sum_radii
180
+
181
+ if current_sum_radii > best_sum_radii_local:
182
+ best_sum_radii_local = current_sum_radii
183
+ best_centers_local = np.copy(current_centers)
184
+
185
+ return best_centers_local, best_sum_radii_local
186
+
187
+ def construct_packing(self):
188
+ """
189
+ Main method to construct the circle packing.
190
+ Orchestrates initial placement, exhaustive search, and local refinement.
191
+ """
192
+ self._initial_grid_placement()
193
+
194
+ if self.n > 25:
195
+ # Stage 1: Exhaustive search for the optimal 26th circle position
196
+ # This identifies a strong starting point for the 26th circle.
197
+ initial_centers_for_refinement, sum_radii_after_exhaustive = self._find_optimal_26th_circle_position()
198
+
199
+ # Stage 2: Local refinement using SA for the 26th circle's position
200
+ # This fine-tunes the position to potentially eke out further gains.
201
+ refined_centers, _ = self._local_refinement_26th_circle(
202
+ initial_centers_for_refinement,
203
+ sum_radii_after_exhaustive,
204
+ index_to_perturb=25
205
+ )
206
+ self.centers = refined_centers # Update the instance's centers with the refined ones
207
+
208
+ # Final radius calculation for the optimized center configuration
209
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
210
+ return self.centers, self.radii
211
+
212
+
213
+ def construct_packing():
214
+ """
215
+ Constructs an arrangement of 26 circles by leveraging a modular
216
+ object-oriented approach. It initializes with a 5x5 grid and
217
+ dynamically places the 26th circle in the most optimal interstitial
218
+ void, then iteratively adjusts radii to maximize their sum.
219
+
220
+ Returns:
221
+ Tuple of (centers, radii)
222
+ centers: np.array of shape (26, 2) with (x, y) coordinates
223
+ radii: np.array of shape (26) with final radius of each circle
224
+ """
225
+ packer = CirclePacker(num_circles=26)
226
+ centers, radii = packer.construct_packing()
227
+ return centers, radii
228
+ # EVOLVE-BLOCK-END
229
+
230
+
231
+ # This part remains fixed (not evolved)
232
+ def run_packing():
233
+ """Run the circle packing constructor for n=26"""
234
+ centers, radii = construct_packing()
235
+ # Calculate the sum of radii
236
+ sum_radii = np.sum(radii)
237
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_101/search_replace.txt ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <exponential_decay_radii_calc>
2
+ A previous version description indicated an exponential decay for the growth factor and tolerance parameters within `_compute_max_radii_static`, but the actual implementation used a linear decay. This change corrects `_compute_max_radii_static` to use an exponential decay for both `current_growth_factor` and `current_tolerance`, aligning the code with the intended and often more effective annealing schedule for iterative radius determination. This smoother, non-linear transition can lead to more precise and robust radius calculations, which are critical for accurate evaluation of packing quality during the SA stages.
3
+ </NAME>
4
+
5
+ <DIFF>
6
+ <<<<<<< SEARCH
7
+ # Calculate dynamic growth factor (linear decay)
8
+ # Factor goes from 0 (at first iter) to 1 (at last iter)
9
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0)
10
+ current_growth_factor = growth_factor_initial - interp_factor * \
11
+ (growth_factor_initial - growth_factor_final)
12
+
13
+ # Calculate dynamic tolerance (linear decay)
14
+ current_tolerance = tolerance_initial - interp_factor * \
15
+ (tolerance_initial - tolerance_final)
16
+ =======
17
+ # Use exponential interpolation for smoother parameter transition
18
+ progress = outer_iter_idx / (outer_iterations - 1 + 1e-9)
19
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**progress
20
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**progress
21
+ >>>>>>> REPLACE
22
+ </DIFF>
23
+
24
+ <add_global_sa_refinement>
25
+ The current implementation lacks a global simulated annealing (SA) refinement stage that perturbs all circles. This omission prevents the packing from breaking free from the initial rigid 5x5 grid and exploring more optimal global arrangements. This edit introduces a `_global_refinement_sa` method, taking inspiration from highly-scoring previous versions. This new stage will apply SA to all 26 circles, using a carefully tuned schedule (more iterations, slightly lower step size and temperature) to gently "jiggle" the entire packing into a better global optimum after the initial placement and local refinement. This will allow the grid structure to deform and the circles to settle into more dense configurations.
26
+ </NAME>
27
+
28
+ <DIFF>
29
+ <<<<<<< SEARCH
30
+ return best_centers_local, best_sum_radii_local
31
+
32
+ def construct_packing(self):
33
+ """
34
+ Main method to construct the circle packing.
35
+ Orchestrates initial placement, exhaustive search, and local refinement.
36
+ """
37
+ =======
38
+ return best_centers_local, best_sum_radii_local
39
+
40
+ def _global_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
41
+ """
42
+ Applies a global, low-temperature SA to "jiggle" all circles into a
43
+ better global optimum, using tuned parameters from high-scoring versions.
44
+ """
45
+ current_centers = np.copy(initial_centers)
46
+ current_sum_radii = initial_sum_radii
47
+
48
+ best_centers_global = np.copy(current_centers)
49
+ best_sum_radii_global = current_sum_radii
50
+
51
+ # SA parameters for a final, gentle, global refinement
52
+ num_iterations = 1500
53
+ initial_step_size = 0.004
54
+ initial_temp = 1e-5
55
+ cooling_rate = 0.995
56
+
57
+ step_size = initial_step_size
58
+ temp = initial_temp
59
+
60
+ for _ in range(num_iterations):
61
+ # Perturb a random circle from the entire set
62
+ idx_to_perturb = np.random.randint(self.n)
63
+
64
+ trial_centers = np.copy(current_centers)
65
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
66
+ trial_centers[idx_to_perturb] += move
67
+ trial_centers[idx_to_perturb] = np.clip(trial_centers[idx_to_perturb], 0.0, 1.0)
68
+
69
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
70
+ trial_sum_radii = np.sum(trial_radii)
71
+
72
+ delta_energy = trial_sum_radii - current_sum_radii
73
+ if delta_energy > 0 or (temp > 1e-9 and np.random.random() < np.exp(delta_energy / temp)):
74
+ current_centers = trial_centers
75
+ current_sum_radii = trial_sum_radii
76
+
77
+ if current_sum_radii > best_sum_radii_global:
78
+ best_sum_radii_global = current_sum_radii
79
+ best_centers_global = np.copy(current_centers)
80
+
81
+ temp *= cooling_rate
82
+ step_size = max(step_size * cooling_rate, 5e-8)
83
+
84
+ return best_centers_global, best_sum_radii_global
85
+
86
+ def construct_packing(self):
87
+ """
88
+ Main method to construct the circle packing.
89
+ Orchestrates initial placement, exhaustive search, and local refinement.
90
+ """
91
+ >>>>>>> REPLACE
92
+ </DIFF>
93
+
94
+ <integrate_global_sa>
95
+ This edit integrates the newly added `_global_refinement_sa` into the `construct_packing` pipeline. After the initial grid placement, exhaustive search for the 26th circle, and local SA refinement of that circle, a final `_global_refinement_sa` stage is introduced. This crucial step will allow all 26 circles to adjust their positions simultaneously, breaking the rigidity of the initial grid and enabling the discovery of higher-scoring, globally optimized packing configurations. This funnel approach, from coarse to fine and local to global refinement, is a hallmark of successful circle packing algorithms.
96
+ </NAME>
97
+
98
+ <DIFF>
99
+ <<<<<<< SEARCH
100
+ # This fine-tunes the position to potentially eke out further gains.
101
+ refined_centers, _ = self._local_refinement_26th_circle(
102
+ initial_centers_for_refinement,
103
+ sum_radii_after_exhaustive,
104
+ index_to_perturb=25
105
+ )
106
+ self.centers = refined_centers # Update the instance's centers with the refined ones
107
+
108
+ # Final radius calculation for the optimized center configuration
109
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
110
+ return self.centers, self.radii
111
+ =======
112
+ # This fine-tunes the position to potentially eke out further gains.
113
+ centers_after_local_refinement, sum_radii_after_local_refinement = self._local_refinement_26th_circle(
114
+ initial_centers_for_refinement,
115
+ sum_radii_after_exhaustive,
116
+ index_to_perturb=25
117
+ )
118
+
119
+ # Stage 3: Global "gentle jiggle" refinement using SA on all circles.
120
+ refined_centers, _ = self._global_refinement_sa(
121
+ centers_after_local_refinement,
122
+ sum_radii_after_local_refinement
123
+ )
124
+ self.centers = refined_centers # Update the instance's centers with the globally refined ones
125
+
126
+ # Final radius calculation for the fully optimized center configuration
127
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
128
+ return self.centers, self.radii
129
+ >>>>>>> REPLACE
130
+ </DIFF>
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_102/edit.diff ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,356 +1,280 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+ from itertools import product
7
+
8
+
9
+ class CirclePacker:
10
+ """
11
+ A class to construct circle packings within a unit square using a hybrid
12
+ optimization approach. It combines an exhaustive search for initial placement
13
+ - with a localized refinement stage.
14
+ + with localized and global refinement stages.
15
+ """
16
+ def __init__(self, num_circles=26):
17
+ self.n = num_circles
18
+ self.centers = np.zeros((self.n, 2))
19
+ self.radii = np.zeros(self.n)
20
+
21
+ @staticmethod
22
+ def _compute_max_radii_static(centers, n):
23
+ """
24
+ Computes the maximum possible radii for a given set of circle centers using
25
+ - an iterative growth and constraint resolution method. This static version
26
+ - incorporates an adaptive growth factor and dynamic tolerance for enhanced
27
+ - performance and precision, based on the best prior implementation.
28
+ + an iterative growth and constraint resolution method. This version uses a
29
+ + tighter final tolerance for enhanced precision.
30
+
31
+ Args:
32
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
33
+ n (int): Number of circles.
34
+
35
+ Returns:
36
+ np.array: An array of shape (n) containing the final radius of each circle.
37
+ """
38
+ radii = np.zeros(n)
39
+
40
+ # Parameters for adaptive growth factor and dynamic tolerance
41
+ growth_factor_initial = 1.005
42
+ growth_factor_final = 1.002
43
+ tolerance_initial = 1e-7
44
+ - tolerance_final = 1e-10
45
+ + tolerance_final = 1e-11 # Increased precision from 1e-10
46
+
47
+ outer_iterations = 400
48
+ inner_iterations = 20
49
+
50
+ # Initialize radii based on the distance to the square's boundaries.
51
+ for i in range(n):
52
+ x, y = centers[i]
53
+ radii[i] = min(x, 1 - x, y, 1 - y)
54
+
55
+ for outer_iter_idx in range(outer_iterations):
56
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0 + 1e-9)
57
+ - # Use exponential decay for smoother, more effective parameter transition, based on prior high-scoring versions.
58
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**interp_factor
59
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**interp_factor
60
+
61
+ - radii *= current_growth_factor # Tentatively grow all radii
62
+ + radii *= current_growth_factor
63
+
64
+ for _inner_iter_idx in range(inner_iterations):
65
+ constraints_changed = False
66
+
67
+ - # Enforce boundary constraints with dynamic tolerance
68
+ for i in range(n):
69
+ x, y = centers[i]
70
+ boundary_limit = min(x, 1 - x, y, 1 - y)
71
+ if radii[i] > boundary_limit + current_tolerance:
72
+ radii[i] = boundary_limit
73
+ constraints_changed = True
74
+
75
+ - # Resolve overlaps between circles with dynamic tolerance
76
+ for i in range(n):
77
+ for j in range(i + 1, n):
78
+ dist = np.linalg.norm(centers[i] - centers[j])
79
+ if radii[i] + radii[j] > dist + current_tolerance:
80
+ total_radius = radii[i] + radii[j]
81
+ if total_radius > tolerance_final:
82
+ scale = dist / total_radius
83
+ radii[i] *= scale
84
+ radii[j] *= scale
85
+ constraints_changed = True
86
+
87
+ if not constraints_changed:
88
+ break
89
+ return radii
90
+
91
+ def _initial_grid_placement(self):
92
+ """
93
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
94
+ """
95
+ coords = np.linspace(0.1, 0.9, 5)
96
+ grid_centers = np.array(list(product(coords, coords)))
97
+ self.centers[:25] = grid_centers
98
+
99
+ def _find_optimal_26th_circle_position(self):
100
+ """
101
+ Performs a multi-resolution grid search to find the best initial position
102
+ for the 26th circle among a dense set of interstitial candidates.
103
+ """
104
+ base_25_centers = np.copy(self.centers[:25])
105
+ -
106
+ - # Initialize with a default position (center of the square) and calculate its sum of radii
107
+ optimal_26th_pos = np.array([0.5, 0.5])
108
+ trial_centers_initial = np.vstack([base_25_centers, optimal_26th_pos])
109
+ trial_radii_initial = CirclePacker._compute_max_radii_static(trial_centers_initial, self.n)
110
+ best_sum_radii = np.sum(trial_radii_initial)
111
+ -
112
+ - # Keep track of the best position from the coarse search to center the fine search
113
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
114
+ -
115
+ - # Expand the coarse search grid to cover a broader area, as per recommendations.
116
+ interstitial_core_coords = np.linspace(0.1, 0.9, 8)
117
+ -
118
+ - # --- Phase 1: Coarse Grid Search ---
119
+ - # Explore a broader region first to identify promising areas.
120
+ coarse_delta = 0.05
121
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta])
122
+ -
123
+ - coarse_candidate_points = []
124
+ - for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
125
+ - for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets):
126
+ - coarse_candidate_points.append([base_x + offset_x, base_y + offset_y])
127
+ + coarse_candidate_points = [
128
+ + [base_x + offset_x, base_y + offset_y]
129
+ + for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords)
130
+ + for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets)
131
+ + ]
132
+
133
+ for candidate_pos in coarse_candidate_points:
134
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
135
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
136
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
137
+ current_sum_radii = np.sum(trial_radii)
138
+ -
139
+ if current_sum_radii > best_sum_radii:
140
+ best_sum_radii = current_sum_radii
141
+ optimal_26th_pos = clipped_candidate_pos
142
+ best_coarse_pos_for_fine_tuning = clipped_candidate_pos
143
+
144
+ - # --- Phase 2: Fine Grid Search around the best coarse position ---
145
+ - # Focus the search more precisely around the most promising area identified in Phase 1.
146
+ fine_delta = 0.01
147
+ fine_perturbation_offsets = np.array([-fine_delta, 0, fine_delta])
148
+ -
149
+ - fine_candidate_points = []
150
+ - for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets):
151
+ - fine_candidate_points.append([best_coarse_pos_for_fine_tuning[0] + offset_x, best_coarse_pos_for_fine_tuning[1] + offset_y])
152
+ + fine_candidate_points = [
153
+ + [best_coarse_pos_for_fine_tuning[0] + offset_x, best_coarse_pos_for_fine_tuning[1] + offset_y]
154
+ + for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets)
155
+ + ]
156
+
157
+ for candidate_pos in fine_candidate_points:
158
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
159
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
160
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
161
+ current_sum_radii = np.sum(trial_radii)
162
+ -
163
+ if current_sum_radii > best_sum_radii:
164
+ best_sum_radii = current_sum_radii
165
+ optimal_26th_pos = clipped_candidate_pos
166
+
167
+ self.centers[25] = optimal_26th_pos
168
+ -
169
+ return self.centers, best_sum_radii
170
+
171
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
172
+ """
173
+ - Applies a localized Simulated Annealing (SA) search to fine-tune the
174
+ - positions of a cluster of circles: the 26th and its nearest neighbors.
175
+ - This allows the base grid to relax and better accommodate the interstitial circle.
176
+ - This version is enhanced with stress-based selection and dynamic step size adjustment.
177
+ + Applies a localized SA search to fine-tune the positions of a cluster of circles.
178
+ """
179
+ current_centers = np.copy(initial_centers)
180
+ - # Compute radii for initial stress calculation and consistent sum.
181
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
182
+ current_sum_radii = np.sum(current_radii)
183
+ -
184
+ best_centers_local = np.copy(current_centers)
185
+ best_sum_radii_local = current_sum_radii
186
+ -
187
+ - # SA parameters adapted from high-performing prior implementations for fine-tuning
188
+ num_iterations = 150
189
+ initial_step_size = 0.005
190
+ initial_temp = 0.0001
191
+ cooling_rate = 0.99
192
+ -
193
+ step_size = initial_step_size
194
+ temp = initial_temp
195
+ -
196
+ - # Identify the cluster: the 26th circle and its 4 closest neighbors.
197
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
198
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
199
+ cluster_indices = np.append(closest_neighbor_indices, 25)
200
+ -
201
+ - # Parameters for dynamic step size adjustment
202
+ - acceptance_window = 30
203
+ - acceptance_count = 0
204
+ - target_acceptance_rate = 0.44
205
+ - adjustment_factor = 1.05
206
+ + acceptance_window, acceptance_count = 30, 0
207
+ + target_acceptance_rate, adjustment_factor = 0.44, 1.05
208
+
209
+ for i in range(num_iterations):
210
+ - # Stress-based selection within the cluster
211
+ cluster_radii = current_radii[cluster_indices]
212
+ inv_radii = 1.0 / (cluster_radii + 1e-9)
213
+ weights = (inv_radii - np.min(inv_radii))**2
214
+ - if np.sum(weights) > 1e-9:
215
+ - probabilities = weights / np.sum(weights)
216
+ - idx_to_move = np.random.choice(cluster_indices, p=probabilities)
217
+ - else:
218
+ - idx_to_move = np.random.choice(cluster_indices)
219
+ -
220
+ + idx_to_move = np.random.choice(cluster_indices, p=weights / np.sum(weights)) if np.sum(weights) > 1e-9 else np.random.choice(cluster_indices)
221
+
222
+ trial_centers = np.copy(current_centers)
223
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
224
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
225
+ -
226
+ - # Evaluate the new configuration
227
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
228
+ trial_sum_radii = np.sum(trial_radii)
229
+ -
230
+ - # Metropolis-Hastings acceptance criterion
231
+ delta_energy = trial_sum_radii - current_sum_radii
232
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
233
+ - current_centers = trial_centers
234
+ - current_radii = trial_radii # Update radii for stress calculation
235
+ - current_sum_radii = trial_sum_radii
236
+ + current_centers, current_radii, current_sum_radii = trial_centers, trial_radii, trial_sum_radii
237
+ acceptance_count += 1
238
+ -
239
+ if current_sum_radii > best_sum_radii_local:
240
+ best_sum_radii_local = current_sum_radii
241
+ best_centers_local = np.copy(current_centers)
242
+
243
+ - # Dynamic step size adjustment
244
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
245
+ - acceptance_rate = acceptance_count / acceptance_window
246
+ - if acceptance_rate > target_acceptance_rate:
247
+ - step_size *= adjustment_factor
248
+ - else:
249
+ - step_size /= adjustment_factor
250
+ + step_size *= adjustment_factor if acceptance_count / acceptance_window > target_acceptance_rate else 1 / adjustment_factor
251
+ acceptance_count = 0
252
+ -
253
+ temp *= cooling_rate
254
+ step_size = max(step_size * cooling_rate, 1e-7)
255
+ -
256
+ return best_centers_local, best_sum_radii_local
257
+
258
+ def _global_refinement_sa(self, initial_centers):
259
+ """
260
+ - Applies a global, low-temperature Simulated Annealing search to fine-tune
261
+ - the positions of ALL circles, acting as a "gentle jiggle" phase to
262
+ - settle the entire packing into a better local optimum. This technique was
263
+ - present in prior high-scoring implementations.
264
+ - """
265
+ - # SA parameters adapted from high-performing prior implementations for a thorough yet gentle search.
266
+ - sa_iterations = 300
267
+ + Applies a global SA search with probabilistic cluster moves to refine all circles.
268
+ + """
269
+ + sa_iterations = 350 # Increased for more thorough search
270
+ sa_initial_temp = 5e-6
271
+ sa_cooling_rate = 0.99
272
+ sa_initial_step_size = 0.001
273
+ -
274
+ current_centers = np.copy(initial_centers)
275
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
276
+ current_sum_radii = np.sum(current_radii)
277
+ -
278
+ - best_centers = np.copy(current_centers)
279
+ - best_sum_radii = current_sum_radii
280
+ -
281
+ - temp = sa_initial_temp
282
+ - step_size = sa_initial_step_size
283
+ -
284
+ + best_centers, best_sum_radii = np.copy(current_centers), current_sum_radii
285
+ + temp, step_size = sa_initial_temp, sa_initial_step_size
286
+ all_indices = np.arange(self.n)
287
+ -
288
+ - # Parameters for dynamic step size adjustment
289
+ - acceptance_window = 30
290
+ - acceptance_count = 0
291
+ - target_acceptance_rate = 0.44 # Common target for SA
292
+ - adjustment_factor = 1.05
293
+ + acceptance_window, acceptance_count = 30, 0
294
+ + target_acceptance_rate, adjustment_factor = 0.44, 1.05
295
+ + cluster_move_prob = 0.15
296
+ + num_cluster_neighbors = 2
297
+
298
+ for i in range(sa_iterations):
299
+ - # Stress-based selection: prioritize moving circles with smaller radii.
300
+ - inv_radii = 1.0 / (current_radii + 1e-9)
301
+ - weights = (inv_radii - np.min(inv_radii))**2
302
+ - if np.sum(weights) > 1e-9:
303
+ - probabilities = weights / np.sum(weights)
304
+ - idx_to_move = np.random.choice(all_indices, p=probabilities)
305
+ + trial_centers = np.copy(current_centers)
306
+ +
307
+ + # Probabilistically choose between a single move and a cluster move
308
+ + if np.random.rand() < cluster_move_prob:
309
+ + # --- Cluster Move ---
310
+ + inv_radii = 1.0 / (current_radii + 1e-9)
311
+ + weights = (inv_radii - np.min(inv_radii))**2
312
+ + primary_idx = np.random.choice(all_indices, p=weights / np.sum(weights)) if np.sum(weights) > 1e-9 else np.random.choice(all_indices)
313
+ + distances = np.linalg.norm(trial_centers - trial_centers[primary_idx], axis=1)
314
+ + distances[primary_idx] = np.inf
315
+ + neighbor_indices = np.argsort(distances)[:num_cluster_neighbors]
316
+ + cluster_indices = np.append(neighbor_indices, primary_idx)
317
+ + move = (np.random.rand(2) - 0.5) * 2 * step_size
318
+ + trial_centers[cluster_indices] += move
319
+ + trial_centers[cluster_indices] = np.clip(trial_centers[cluster_indices], 0.0, 1.0)
320
+ else:
321
+ - idx_to_move = np.random.choice(all_indices)
322
+ -
323
+ - trial_centers = np.copy(current_centers)
324
+ - move = (np.random.rand(2) - 0.5) * 2 * step_size
325
+ - trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
326
+ -
327
+ + # --- Single Move ---
328
+ + inv_radii = 1.0 / (current_radii + 1e-9)
329
+ + weights = (inv_radii - np.min(inv_radii))**2
330
+ + idx_to_move = np.random.choice(all_indices, p=weights / np.sum(weights)) if np.sum(weights) > 1e-9 else np.random.choice(all_indices)
331
+ + move = (np.random.rand(2) - 0.5) * 2 * step_size
332
+ + trial_centers[idx_to_move] += move
333
+ + trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move], 0.0, 1.0)
334
+ +
335
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
336
+ trial_sum_radii = np.sum(trial_radii)
337
+ -
338
+ delta_energy = trial_sum_radii - current_sum_radii
339
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
340
+ - current_centers = trial_centers
341
+ - current_radii = trial_radii # Keep radii in sync for stress calculation
342
+ - current_sum_radii = trial_sum_radii
343
+ + current_centers, current_radii, current_sum_radii = trial_centers, trial_radii, trial_sum_radii
344
+ acceptance_count += 1
345
+ -
346
+ if current_sum_radii > best_sum_radii:
347
+ best_sum_radii = current_sum_radii
348
+ best_centers = np.copy(current_centers)
349
+
350
+ - # Periodically adjust step_size based on acceptance rate to balance exploration/exploitation.
351
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
352
+ - acceptance_rate = acceptance_count / acceptance_window
353
+ - if acceptance_rate > target_acceptance_rate:
354
+ - step_size *= adjustment_factor
355
+ - else:
356
+ - step_size /= adjustment_factor
357
+ + step_size *= adjustment_factor if acceptance_count / acceptance_window > target_acceptance_rate else 1/adjustment_factor
358
+ acceptance_count = 0
359
+
360
+ temp *= sa_cooling_rate
361
+ - step_size = max(step_size * sa_cooling_rate, 1e-8) # Maintain overall cooling trend for step size
362
+ -
363
+ + step_size = max(step_size * sa_cooling_rate, 1e-8)
364
+ return best_centers
365
+
366
+ def construct_packing(self):
367
+ """
368
+ - Main method to construct the circle packing, orchestrating a multi-stage
369
+ - optimization: initial placement, multi-res search, local SA, and global SA.
370
+ + Orchestrates the multi-stage optimization process.
371
+ """
372
+ self._initial_grid_placement()
373
+ -
374
+ if self.n > 25:
375
+ - # Stage 1: Multi-resolution search for the 26th circle's initial position
376
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
377
+ -
378
+ - # Stage 2: Local refinement of the interstitial circle and its neighbors using SA
379
+ centers_after_local_sa, _ = self._local_refinement_cluster_sa(
380
+ centers_after_search,
381
+ sum_radii_after_search
382
+ )
383
+ -
384
+ - # Stage 3: Global "gentle jiggle" refinement on all circles using SA
385
+ centers_after_global_sa = self._global_refinement_sa(centers_after_local_sa)
386
+ self.centers = centers_after_global_sa
387
+
388
+ - # Final radius calculation for the fully optimized center configuration
389
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
390
+ return self.centers, self.radii
391
+
392
+
393
+ def construct_packing():
394
+ """
395
+ - Constructs an arrangement of 26 circles by leveraging a superior three-stage
396
+ - optimization strategy: initial grid, dense interstitial search, and localized SA.
397
+ -
398
+ - Returns:
399
+ - Tuple of (centers, radii)
400
+ - centers: np.array of shape (26, 2) with (x, y) coordinates
401
+ - radii: np.array of shape (26) with final radius of each circle
402
+ + Constructs an arrangement of 26 circles by leveraging a superior multi-stage
403
+ + optimization strategy including grid search, local SA, and global SA with cluster moves.
404
+ """
405
+ packer = CirclePacker(num_circles=26)
406
+ centers, radii = packer.construct_packing()
407
+ return centers, radii
408
+ # EVOLVE-BLOCK-END
409
+
410
+
411
+ # This part remains fixed (not evolved)
412
+ def run_packing():
413
+ """Run the circle packing constructor for n=26"""
414
+ centers, radii = construct_packing()
415
+ # Calculate the sum of radii
416
+ sum_radii = np.sum(radii)
417
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_102/main.py ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings within a unit square using a hybrid
9
+ optimization approach. It combines an exhaustive search for initial placement
10
+ with localized and global refinement stages.
11
+ """
12
+ def __init__(self, num_circles=26):
13
+ self.n = num_circles
14
+ self.centers = np.zeros((self.n, 2))
15
+ self.radii = np.zeros(self.n)
16
+
17
+ @staticmethod
18
+ def _compute_max_radii_static(centers, n):
19
+ """
20
+ Computes the maximum possible radii for a given set of circle centers using
21
+ an iterative growth and constraint resolution method. This version uses a
22
+ tighter final tolerance for enhanced precision.
23
+
24
+ Args:
25
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
26
+ n (int): Number of circles.
27
+
28
+ Returns:
29
+ np.array: An array of shape (n) containing the final radius of each circle.
30
+ """
31
+ radii = np.zeros(n)
32
+
33
+ # Parameters for adaptive growth factor and dynamic tolerance
34
+ growth_factor_initial = 1.005
35
+ growth_factor_final = 1.002
36
+ tolerance_initial = 1e-7
37
+ tolerance_final = 1e-11 # Increased precision from 1e-10
38
+
39
+ outer_iterations = 400
40
+ inner_iterations = 20
41
+
42
+ # Initialize radii based on the distance to the square's boundaries.
43
+ for i in range(n):
44
+ x, y = centers[i]
45
+ radii[i] = min(x, 1 - x, y, 1 - y)
46
+
47
+ for outer_iter_idx in range(outer_iterations):
48
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0 + 1e-9)
49
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**interp_factor
50
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**interp_factor
51
+
52
+ radii *= current_growth_factor
53
+
54
+ for _inner_iter_idx in range(inner_iterations):
55
+ constraints_changed = False
56
+
57
+ for i in range(n):
58
+ x, y = centers[i]
59
+ boundary_limit = min(x, 1 - x, y, 1 - y)
60
+ if radii[i] > boundary_limit + current_tolerance:
61
+ radii[i] = boundary_limit
62
+ constraints_changed = True
63
+
64
+ for i in range(n):
65
+ for j in range(i + 1, n):
66
+ dist = np.linalg.norm(centers[i] - centers[j])
67
+ if radii[i] + radii[j] > dist + current_tolerance:
68
+ total_radius = radii[i] + radii[j]
69
+ if total_radius > tolerance_final:
70
+ scale = dist / total_radius
71
+ radii[i] *= scale
72
+ radii[j] *= scale
73
+ constraints_changed = True
74
+
75
+ if not constraints_changed:
76
+ break
77
+ return radii
78
+
79
+ def _initial_grid_placement(self):
80
+ """
81
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
82
+ """
83
+ coords = np.linspace(0.1, 0.9, 5)
84
+ grid_centers = np.array(list(product(coords, coords)))
85
+ self.centers[:25] = grid_centers
86
+
87
+ def _find_optimal_26th_circle_position(self):
88
+ """
89
+ Performs a multi-resolution grid search to find the best initial position
90
+ for the 26th circle among a dense set of interstitial candidates.
91
+ """
92
+ base_25_centers = np.copy(self.centers[:25])
93
+ optimal_26th_pos = np.array([0.5, 0.5])
94
+ trial_centers_initial = np.vstack([base_25_centers, optimal_26th_pos])
95
+ trial_radii_initial = CirclePacker._compute_max_radii_static(trial_centers_initial, self.n)
96
+ best_sum_radii = np.sum(trial_radii_initial)
97
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
98
+ interstitial_core_coords = np.linspace(0.1, 0.9, 8)
99
+ coarse_delta = 0.05
100
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta])
101
+ coarse_candidate_points = [
102
+ [base_x + offset_x, base_y + offset_y]
103
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords)
104
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets)
105
+ ]
106
+
107
+ for candidate_pos in coarse_candidate_points:
108
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
109
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
110
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
111
+ current_sum_radii = np.sum(trial_radii)
112
+ if current_sum_radii > best_sum_radii:
113
+ best_sum_radii = current_sum_radii
114
+ optimal_26th_pos = clipped_candidate_pos
115
+ best_coarse_pos_for_fine_tuning = clipped_candidate_pos
116
+
117
+ fine_delta = 0.01
118
+ fine_perturbation_offsets = np.array([-fine_delta, 0, fine_delta])
119
+ fine_candidate_points = [
120
+ [best_coarse_pos_for_fine_tuning[0] + offset_x, best_coarse_pos_for_fine_tuning[1] + offset_y]
121
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets)
122
+ ]
123
+
124
+ for candidate_pos in fine_candidate_points:
125
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
126
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
127
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
128
+ current_sum_radii = np.sum(trial_radii)
129
+ if current_sum_radii > best_sum_radii:
130
+ best_sum_radii = current_sum_radii
131
+ optimal_26th_pos = clipped_candidate_pos
132
+
133
+ self.centers[25] = optimal_26th_pos
134
+ return self.centers, best_sum_radii
135
+
136
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
137
+ """
138
+ Applies a localized SA search to fine-tune the positions of a cluster of circles.
139
+ """
140
+ current_centers = np.copy(initial_centers)
141
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
142
+ current_sum_radii = np.sum(current_radii)
143
+ best_centers_local = np.copy(current_centers)
144
+ best_sum_radii_local = current_sum_radii
145
+ num_iterations = 150
146
+ initial_step_size = 0.005
147
+ initial_temp = 0.0001
148
+ cooling_rate = 0.99
149
+ step_size = initial_step_size
150
+ temp = initial_temp
151
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
152
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
153
+ cluster_indices = np.append(closest_neighbor_indices, 25)
154
+ acceptance_window, acceptance_count = 30, 0
155
+ target_acceptance_rate, adjustment_factor = 0.44, 1.05
156
+
157
+ for i in range(num_iterations):
158
+ cluster_radii = current_radii[cluster_indices]
159
+ inv_radii = 1.0 / (cluster_radii + 1e-9)
160
+ weights = (inv_radii - np.min(inv_radii))**2
161
+ idx_to_move = np.random.choice(cluster_indices, p=weights / np.sum(weights)) if np.sum(weights) > 1e-9 else np.random.choice(cluster_indices)
162
+
163
+ trial_centers = np.copy(current_centers)
164
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
165
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
166
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
167
+ trial_sum_radii = np.sum(trial_radii)
168
+ delta_energy = trial_sum_radii - current_sum_radii
169
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
170
+ current_centers, current_radii, current_sum_radii = trial_centers, trial_radii, trial_sum_radii
171
+ acceptance_count += 1
172
+ if current_sum_radii > best_sum_radii_local:
173
+ best_sum_radii_local = current_sum_radii
174
+ best_centers_local = np.copy(current_centers)
175
+
176
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
177
+ step_size *= adjustment_factor if acceptance_count / acceptance_window > target_acceptance_rate else 1 / adjustment_factor
178
+ acceptance_count = 0
179
+ temp *= cooling_rate
180
+ step_size = max(step_size * cooling_rate, 1e-7)
181
+ return best_centers_local, best_sum_radii_local
182
+
183
+ def _global_refinement_sa(self, initial_centers):
184
+ """
185
+ Applies a global SA search with probabilistic cluster moves to refine all circles.
186
+ """
187
+ sa_iterations = 350 # Increased for more thorough search
188
+ sa_initial_temp = 5e-6
189
+ sa_cooling_rate = 0.99
190
+ sa_initial_step_size = 0.001
191
+ current_centers = np.copy(initial_centers)
192
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
193
+ current_sum_radii = np.sum(current_radii)
194
+ best_centers, best_sum_radii = np.copy(current_centers), current_sum_radii
195
+ temp, step_size = sa_initial_temp, sa_initial_step_size
196
+ all_indices = np.arange(self.n)
197
+ acceptance_window, acceptance_count = 30, 0
198
+ target_acceptance_rate, adjustment_factor = 0.44, 1.05
199
+ cluster_move_prob = 0.15
200
+ num_cluster_neighbors = 2
201
+
202
+ for i in range(sa_iterations):
203
+ trial_centers = np.copy(current_centers)
204
+
205
+ # Probabilistically choose between a single move and a cluster move
206
+ if np.random.rand() < cluster_move_prob:
207
+ # --- Cluster Move ---
208
+ inv_radii = 1.0 / (current_radii + 1e-9)
209
+ weights = (inv_radii - np.min(inv_radii))**2
210
+ primary_idx = np.random.choice(all_indices, p=weights / np.sum(weights)) if np.sum(weights) > 1e-9 else np.random.choice(all_indices)
211
+ distances = np.linalg.norm(trial_centers - trial_centers[primary_idx], axis=1)
212
+ distances[primary_idx] = np.inf
213
+ neighbor_indices = np.argsort(distances)[:num_cluster_neighbors]
214
+ cluster_indices = np.append(neighbor_indices, primary_idx)
215
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
216
+ trial_centers[cluster_indices] += move
217
+ trial_centers[cluster_indices] = np.clip(trial_centers[cluster_indices], 0.0, 1.0)
218
+ else:
219
+ # --- Single Move ---
220
+ inv_radii = 1.0 / (current_radii + 1e-9)
221
+ weights = (inv_radii - np.min(inv_radii))**2
222
+ idx_to_move = np.random.choice(all_indices, p=weights / np.sum(weights)) if np.sum(weights) > 1e-9 else np.random.choice(all_indices)
223
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
224
+ trial_centers[idx_to_move] += move
225
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move], 0.0, 1.0)
226
+
227
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
228
+ trial_sum_radii = np.sum(trial_radii)
229
+ delta_energy = trial_sum_radii - current_sum_radii
230
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
231
+ current_centers, current_radii, current_sum_radii = trial_centers, trial_radii, trial_sum_radii
232
+ acceptance_count += 1
233
+ if current_sum_radii > best_sum_radii:
234
+ best_sum_radii = current_sum_radii
235
+ best_centers = np.copy(current_centers)
236
+
237
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
238
+ step_size *= adjustment_factor if acceptance_count / acceptance_window > target_acceptance_rate else 1/adjustment_factor
239
+ acceptance_count = 0
240
+
241
+ temp *= sa_cooling_rate
242
+ step_size = max(step_size * sa_cooling_rate, 1e-8)
243
+ return best_centers
244
+
245
+ def construct_packing(self):
246
+ """
247
+ Orchestrates the multi-stage optimization process.
248
+ """
249
+ self._initial_grid_placement()
250
+ if self.n > 25:
251
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
252
+ centers_after_local_sa, _ = self._local_refinement_cluster_sa(
253
+ centers_after_search,
254
+ sum_radii_after_search
255
+ )
256
+ centers_after_global_sa = self._global_refinement_sa(centers_after_local_sa)
257
+ self.centers = centers_after_global_sa
258
+
259
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
260
+ return self.centers, self.radii
261
+
262
+
263
+ def construct_packing():
264
+ """
265
+ Constructs an arrangement of 26 circles by leveraging a superior multi-stage
266
+ optimization strategy including grid search, local SA, and global SA with cluster moves.
267
+ """
268
+ packer = CirclePacker(num_circles=26)
269
+ centers, radii = packer.construct_packing()
270
+ return centers, radii
271
+ # EVOLVE-BLOCK-END
272
+
273
+
274
+ # This part remains fixed (not evolved)
275
+ def run_packing():
276
+ """Run the circle packing constructor for n=26"""
277
+ centers, radii = construct_packing()
278
+ # Calculate the sum of radii
279
+ sum_radii = np.sum(radii)
280
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_102/original.py ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings within a unit square using a hybrid
9
+ optimization approach. It combines an exhaustive search for initial placement
10
+ with a localized refinement stage.
11
+ """
12
+ def __init__(self, num_circles=26):
13
+ self.n = num_circles
14
+ self.centers = np.zeros((self.n, 2))
15
+ self.radii = np.zeros(self.n)
16
+
17
+ @staticmethod
18
+ def _compute_max_radii_static(centers, n):
19
+ """
20
+ Computes the maximum possible radii for a given set of circle centers using
21
+ an iterative growth and constraint resolution method. This static version
22
+ incorporates an adaptive growth factor and dynamic tolerance for enhanced
23
+ performance and precision, based on the best prior implementation.
24
+
25
+ Args:
26
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
27
+ n (int): Number of circles.
28
+
29
+ Returns:
30
+ np.array: An array of shape (n) containing the final radius of each circle.
31
+ """
32
+ radii = np.zeros(n)
33
+
34
+ # Parameters for adaptive growth factor and dynamic tolerance
35
+ growth_factor_initial = 1.005
36
+ growth_factor_final = 1.002
37
+ tolerance_initial = 1e-7
38
+ tolerance_final = 1e-10
39
+
40
+ outer_iterations = 400
41
+ inner_iterations = 20
42
+
43
+ # Initialize radii based on the distance to the square's boundaries.
44
+ for i in range(n):
45
+ x, y = centers[i]
46
+ radii[i] = min(x, 1 - x, y, 1 - y)
47
+
48
+ for outer_iter_idx in range(outer_iterations):
49
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0 + 1e-9)
50
+ # Use exponential decay for smoother, more effective parameter transition, based on prior high-scoring versions.
51
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**interp_factor
52
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**interp_factor
53
+
54
+ radii *= current_growth_factor # Tentatively grow all radii
55
+
56
+ for _inner_iter_idx in range(inner_iterations):
57
+ constraints_changed = False
58
+
59
+ # Enforce boundary constraints with dynamic tolerance
60
+ for i in range(n):
61
+ x, y = centers[i]
62
+ boundary_limit = min(x, 1 - x, y, 1 - y)
63
+ if radii[i] > boundary_limit + current_tolerance:
64
+ radii[i] = boundary_limit
65
+ constraints_changed = True
66
+
67
+ # Resolve overlaps between circles with dynamic tolerance
68
+ for i in range(n):
69
+ for j in range(i + 1, n):
70
+ dist = np.linalg.norm(centers[i] - centers[j])
71
+ if radii[i] + radii[j] > dist + current_tolerance:
72
+ total_radius = radii[i] + radii[j]
73
+ if total_radius > tolerance_final:
74
+ scale = dist / total_radius
75
+ radii[i] *= scale
76
+ radii[j] *= scale
77
+ constraints_changed = True
78
+
79
+ if not constraints_changed:
80
+ break
81
+ return radii
82
+
83
+ def _initial_grid_placement(self):
84
+ """
85
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
86
+ """
87
+ coords = np.linspace(0.1, 0.9, 5)
88
+ grid_centers = np.array(list(product(coords, coords)))
89
+ self.centers[:25] = grid_centers
90
+
91
+ def _find_optimal_26th_circle_position(self):
92
+ """
93
+ Performs a multi-resolution grid search to find the best initial position
94
+ for the 26th circle among a dense set of interstitial candidates.
95
+ """
96
+ base_25_centers = np.copy(self.centers[:25])
97
+
98
+ # Initialize with a default position (center of the square) and calculate its sum of radii
99
+ optimal_26th_pos = np.array([0.5, 0.5])
100
+ trial_centers_initial = np.vstack([base_25_centers, optimal_26th_pos])
101
+ trial_radii_initial = CirclePacker._compute_max_radii_static(trial_centers_initial, self.n)
102
+ best_sum_radii = np.sum(trial_radii_initial)
103
+
104
+ # Keep track of the best position from the coarse search to center the fine search
105
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
106
+
107
+ # Expand the coarse search grid to cover a broader area, as per recommendations.
108
+ interstitial_core_coords = np.linspace(0.1, 0.9, 8)
109
+
110
+ # --- Phase 1: Coarse Grid Search ---
111
+ # Explore a broader region first to identify promising areas.
112
+ coarse_delta = 0.05
113
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta])
114
+
115
+ coarse_candidate_points = []
116
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
117
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets):
118
+ coarse_candidate_points.append([base_x + offset_x, base_y + offset_y])
119
+
120
+ for candidate_pos in coarse_candidate_points:
121
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
122
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
123
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
124
+ current_sum_radii = np.sum(trial_radii)
125
+
126
+ if current_sum_radii > best_sum_radii:
127
+ best_sum_radii = current_sum_radii
128
+ optimal_26th_pos = clipped_candidate_pos
129
+ best_coarse_pos_for_fine_tuning = clipped_candidate_pos
130
+
131
+ # --- Phase 2: Fine Grid Search around the best coarse position ---
132
+ # Focus the search more precisely around the most promising area identified in Phase 1.
133
+ fine_delta = 0.01
134
+ fine_perturbation_offsets = np.array([-fine_delta, 0, fine_delta])
135
+
136
+ fine_candidate_points = []
137
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets):
138
+ fine_candidate_points.append([best_coarse_pos_for_fine_tuning[0] + offset_x, best_coarse_pos_for_fine_tuning[1] + offset_y])
139
+
140
+ for candidate_pos in fine_candidate_points:
141
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
142
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
143
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
144
+ current_sum_radii = np.sum(trial_radii)
145
+
146
+ if current_sum_radii > best_sum_radii:
147
+ best_sum_radii = current_sum_radii
148
+ optimal_26th_pos = clipped_candidate_pos
149
+
150
+ self.centers[25] = optimal_26th_pos
151
+
152
+ return self.centers, best_sum_radii
153
+
154
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
155
+ """
156
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
157
+ positions of a cluster of circles: the 26th and its nearest neighbors.
158
+ This allows the base grid to relax and better accommodate the interstitial circle.
159
+ This version is enhanced with stress-based selection and dynamic step size adjustment.
160
+ """
161
+ current_centers = np.copy(initial_centers)
162
+ # Compute radii for initial stress calculation and consistent sum.
163
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
164
+ current_sum_radii = np.sum(current_radii)
165
+
166
+ best_centers_local = np.copy(current_centers)
167
+ best_sum_radii_local = current_sum_radii
168
+
169
+ # SA parameters adapted from high-performing prior implementations for fine-tuning
170
+ num_iterations = 150
171
+ initial_step_size = 0.005
172
+ initial_temp = 0.0001
173
+ cooling_rate = 0.99
174
+
175
+ step_size = initial_step_size
176
+ temp = initial_temp
177
+
178
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
179
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
180
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
181
+ cluster_indices = np.append(closest_neighbor_indices, 25)
182
+
183
+ # Parameters for dynamic step size adjustment
184
+ acceptance_window = 30
185
+ acceptance_count = 0
186
+ target_acceptance_rate = 0.44
187
+ adjustment_factor = 1.05
188
+
189
+ for i in range(num_iterations):
190
+ # Stress-based selection within the cluster
191
+ cluster_radii = current_radii[cluster_indices]
192
+ inv_radii = 1.0 / (cluster_radii + 1e-9)
193
+ weights = (inv_radii - np.min(inv_radii))**2
194
+ if np.sum(weights) > 1e-9:
195
+ probabilities = weights / np.sum(weights)
196
+ idx_to_move = np.random.choice(cluster_indices, p=probabilities)
197
+ else:
198
+ idx_to_move = np.random.choice(cluster_indices)
199
+
200
+
201
+ trial_centers = np.copy(current_centers)
202
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
203
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
204
+
205
+ # Evaluate the new configuration
206
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
207
+ trial_sum_radii = np.sum(trial_radii)
208
+
209
+ # Metropolis-Hastings acceptance criterion
210
+ delta_energy = trial_sum_radii - current_sum_radii
211
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
212
+ current_centers = trial_centers
213
+ current_radii = trial_radii # Update radii for stress calculation
214
+ current_sum_radii = trial_sum_radii
215
+ acceptance_count += 1
216
+
217
+ if current_sum_radii > best_sum_radii_local:
218
+ best_sum_radii_local = current_sum_radii
219
+ best_centers_local = np.copy(current_centers)
220
+
221
+ # Dynamic step size adjustment
222
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
223
+ acceptance_rate = acceptance_count / acceptance_window
224
+ if acceptance_rate > target_acceptance_rate:
225
+ step_size *= adjustment_factor
226
+ else:
227
+ step_size /= adjustment_factor
228
+ acceptance_count = 0
229
+
230
+ temp *= cooling_rate
231
+ step_size = max(step_size * cooling_rate, 1e-7)
232
+
233
+ return best_centers_local, best_sum_radii_local
234
+
235
+ def _global_refinement_sa(self, initial_centers):
236
+ """
237
+ Applies a global, low-temperature Simulated Annealing search to fine-tune
238
+ the positions of ALL circles, acting as a "gentle jiggle" phase to
239
+ settle the entire packing into a better local optimum. This technique was
240
+ present in prior high-scoring implementations.
241
+ """
242
+ # SA parameters adapted from high-performing prior implementations for a thorough yet gentle search.
243
+ sa_iterations = 300
244
+ sa_initial_temp = 5e-6
245
+ sa_cooling_rate = 0.99
246
+ sa_initial_step_size = 0.001
247
+
248
+ current_centers = np.copy(initial_centers)
249
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
250
+ current_sum_radii = np.sum(current_radii)
251
+
252
+ best_centers = np.copy(current_centers)
253
+ best_sum_radii = current_sum_radii
254
+
255
+ temp = sa_initial_temp
256
+ step_size = sa_initial_step_size
257
+
258
+ all_indices = np.arange(self.n)
259
+
260
+ # Parameters for dynamic step size adjustment
261
+ acceptance_window = 30
262
+ acceptance_count = 0
263
+ target_acceptance_rate = 0.44 # Common target for SA
264
+ adjustment_factor = 1.05
265
+
266
+ for i in range(sa_iterations):
267
+ # Stress-based selection: prioritize moving circles with smaller radii.
268
+ inv_radii = 1.0 / (current_radii + 1e-9)
269
+ weights = (inv_radii - np.min(inv_radii))**2
270
+ if np.sum(weights) > 1e-9:
271
+ probabilities = weights / np.sum(weights)
272
+ idx_to_move = np.random.choice(all_indices, p=probabilities)
273
+ else:
274
+ idx_to_move = np.random.choice(all_indices)
275
+
276
+ trial_centers = np.copy(current_centers)
277
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
278
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
279
+
280
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
281
+ trial_sum_radii = np.sum(trial_radii)
282
+
283
+ delta_energy = trial_sum_radii - current_sum_radii
284
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
285
+ current_centers = trial_centers
286
+ current_radii = trial_radii # Keep radii in sync for stress calculation
287
+ current_sum_radii = trial_sum_radii
288
+ acceptance_count += 1
289
+
290
+ if current_sum_radii > best_sum_radii:
291
+ best_sum_radii = current_sum_radii
292
+ best_centers = np.copy(current_centers)
293
+
294
+ # Periodically adjust step_size based on acceptance rate to balance exploration/exploitation.
295
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
296
+ acceptance_rate = acceptance_count / acceptance_window
297
+ if acceptance_rate > target_acceptance_rate:
298
+ step_size *= adjustment_factor
299
+ else:
300
+ step_size /= adjustment_factor
301
+ acceptance_count = 0
302
+
303
+ temp *= sa_cooling_rate
304
+ step_size = max(step_size * sa_cooling_rate, 1e-8) # Maintain overall cooling trend for step size
305
+
306
+ return best_centers
307
+
308
+ def construct_packing(self):
309
+ """
310
+ Main method to construct the circle packing, orchestrating a multi-stage
311
+ optimization: initial placement, multi-res search, local SA, and global SA.
312
+ """
313
+ self._initial_grid_placement()
314
+
315
+ if self.n > 25:
316
+ # Stage 1: Multi-resolution search for the 26th circle's initial position
317
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
318
+
319
+ # Stage 2: Local refinement of the interstitial circle and its neighbors using SA
320
+ centers_after_local_sa, _ = self._local_refinement_cluster_sa(
321
+ centers_after_search,
322
+ sum_radii_after_search
323
+ )
324
+
325
+ # Stage 3: Global "gentle jiggle" refinement on all circles using SA
326
+ centers_after_global_sa = self._global_refinement_sa(centers_after_local_sa)
327
+ self.centers = centers_after_global_sa
328
+
329
+ # Final radius calculation for the fully optimized center configuration
330
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
331
+ return self.centers, self.radii
332
+
333
+
334
+ def construct_packing():
335
+ """
336
+ Constructs an arrangement of 26 circles by leveraging a superior three-stage
337
+ optimization strategy: initial grid, dense interstitial search, and localized SA.
338
+
339
+ Returns:
340
+ Tuple of (centers, radii)
341
+ centers: np.array of shape (26, 2) with (x, y) coordinates
342
+ radii: np.array of shape (26) with final radius of each circle
343
+ """
344
+ packer = CirclePacker(num_circles=26)
345
+ centers, radii = packer.construct_packing()
346
+ return centers, radii
347
+ # EVOLVE-BLOCK-END
348
+
349
+
350
+ # This part remains fixed (not evolved)
351
+ def run_packing():
352
+ """Run the circle packing constructor for n=26"""
353
+ centers, radii = construct_packing()
354
+ # Calculate the sum of radii
355
+ sum_radii = np.sum(radii)
356
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_102/rewrite.txt ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings within a unit square using a hybrid
9
+ optimization approach. It combines an exhaustive search for initial placement
10
+ with localized and global refinement stages.
11
+ """
12
+ def __init__(self, num_circles=26):
13
+ self.n = num_circles
14
+ self.centers = np.zeros((self.n, 2))
15
+ self.radii = np.zeros(self.n)
16
+
17
+ @staticmethod
18
+ def _compute_max_radii_static(centers, n):
19
+ """
20
+ Computes the maximum possible radii for a given set of circle centers using
21
+ an iterative growth and constraint resolution method. This version uses a
22
+ tighter final tolerance for enhanced precision.
23
+
24
+ Args:
25
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
26
+ n (int): Number of circles.
27
+
28
+ Returns:
29
+ np.array: An array of shape (n) containing the final radius of each circle.
30
+ """
31
+ radii = np.zeros(n)
32
+
33
+ # Parameters for adaptive growth factor and dynamic tolerance
34
+ growth_factor_initial = 1.005
35
+ growth_factor_final = 1.002
36
+ tolerance_initial = 1e-7
37
+ tolerance_final = 1e-11 # Increased precision from 1e-10
38
+
39
+ outer_iterations = 400
40
+ inner_iterations = 20
41
+
42
+ # Initialize radii based on the distance to the square's boundaries.
43
+ for i in range(n):
44
+ x, y = centers[i]
45
+ radii[i] = min(x, 1 - x, y, 1 - y)
46
+
47
+ for outer_iter_idx in range(outer_iterations):
48
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0 + 1e-9)
49
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**interp_factor
50
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**interp_factor
51
+
52
+ radii *= current_growth_factor
53
+
54
+ for _inner_iter_idx in range(inner_iterations):
55
+ constraints_changed = False
56
+
57
+ for i in range(n):
58
+ x, y = centers[i]
59
+ boundary_limit = min(x, 1 - x, y, 1 - y)
60
+ if radii[i] > boundary_limit + current_tolerance:
61
+ radii[i] = boundary_limit
62
+ constraints_changed = True
63
+
64
+ for i in range(n):
65
+ for j in range(i + 1, n):
66
+ dist = np.linalg.norm(centers[i] - centers[j])
67
+ if radii[i] + radii[j] > dist + current_tolerance:
68
+ total_radius = radii[i] + radii[j]
69
+ if total_radius > tolerance_final:
70
+ scale = dist / total_radius
71
+ radii[i] *= scale
72
+ radii[j] *= scale
73
+ constraints_changed = True
74
+
75
+ if not constraints_changed:
76
+ break
77
+ return radii
78
+
79
+ def _initial_grid_placement(self):
80
+ """
81
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
82
+ """
83
+ coords = np.linspace(0.1, 0.9, 5)
84
+ grid_centers = np.array(list(product(coords, coords)))
85
+ self.centers[:25] = grid_centers
86
+
87
+ def _find_optimal_26th_circle_position(self):
88
+ """
89
+ Performs a multi-resolution grid search to find the best initial position
90
+ for the 26th circle among a dense set of interstitial candidates.
91
+ """
92
+ base_25_centers = np.copy(self.centers[:25])
93
+ optimal_26th_pos = np.array([0.5, 0.5])
94
+ trial_centers_initial = np.vstack([base_25_centers, optimal_26th_pos])
95
+ trial_radii_initial = CirclePacker._compute_max_radii_static(trial_centers_initial, self.n)
96
+ best_sum_radii = np.sum(trial_radii_initial)
97
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
98
+ interstitial_core_coords = np.linspace(0.1, 0.9, 8)
99
+ coarse_delta = 0.05
100
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta])
101
+ coarse_candidate_points = [
102
+ [base_x + offset_x, base_y + offset_y]
103
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords)
104
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets)
105
+ ]
106
+
107
+ for candidate_pos in coarse_candidate_points:
108
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
109
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
110
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
111
+ current_sum_radii = np.sum(trial_radii)
112
+ if current_sum_radii > best_sum_radii:
113
+ best_sum_radii = current_sum_radii
114
+ optimal_26th_pos = clipped_candidate_pos
115
+ best_coarse_pos_for_fine_tuning = clipped_candidate_pos
116
+
117
+ fine_delta = 0.01
118
+ fine_perturbation_offsets = np.array([-fine_delta, 0, fine_delta])
119
+ fine_candidate_points = [
120
+ [best_coarse_pos_for_fine_tuning[0] + offset_x, best_coarse_pos_for_fine_tuning[1] + offset_y]
121
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets)
122
+ ]
123
+
124
+ for candidate_pos in fine_candidate_points:
125
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
126
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
127
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
128
+ current_sum_radii = np.sum(trial_radii)
129
+ if current_sum_radii > best_sum_radii:
130
+ best_sum_radii = current_sum_radii
131
+ optimal_26th_pos = clipped_candidate_pos
132
+
133
+ self.centers[25] = optimal_26th_pos
134
+ return self.centers, best_sum_radii
135
+
136
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
137
+ """
138
+ Applies a localized SA search to fine-tune the positions of a cluster of circles.
139
+ """
140
+ current_centers = np.copy(initial_centers)
141
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
142
+ current_sum_radii = np.sum(current_radii)
143
+ best_centers_local = np.copy(current_centers)
144
+ best_sum_radii_local = current_sum_radii
145
+ num_iterations = 150
146
+ initial_step_size = 0.005
147
+ initial_temp = 0.0001
148
+ cooling_rate = 0.99
149
+ step_size = initial_step_size
150
+ temp = initial_temp
151
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
152
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
153
+ cluster_indices = np.append(closest_neighbor_indices, 25)
154
+ acceptance_window, acceptance_count = 30, 0
155
+ target_acceptance_rate, adjustment_factor = 0.44, 1.05
156
+
157
+ for i in range(num_iterations):
158
+ cluster_radii = current_radii[cluster_indices]
159
+ inv_radii = 1.0 / (cluster_radii + 1e-9)
160
+ weights = (inv_radii - np.min(inv_radii))**2
161
+ idx_to_move = np.random.choice(cluster_indices, p=weights / np.sum(weights)) if np.sum(weights) > 1e-9 else np.random.choice(cluster_indices)
162
+
163
+ trial_centers = np.copy(current_centers)
164
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
165
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
166
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
167
+ trial_sum_radii = np.sum(trial_radii)
168
+ delta_energy = trial_sum_radii - current_sum_radii
169
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
170
+ current_centers, current_radii, current_sum_radii = trial_centers, trial_radii, trial_sum_radii
171
+ acceptance_count += 1
172
+ if current_sum_radii > best_sum_radii_local:
173
+ best_sum_radii_local = current_sum_radii
174
+ best_centers_local = np.copy(current_centers)
175
+
176
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
177
+ step_size *= adjustment_factor if acceptance_count / acceptance_window > target_acceptance_rate else 1 / adjustment_factor
178
+ acceptance_count = 0
179
+ temp *= cooling_rate
180
+ step_size = max(step_size * cooling_rate, 1e-7)
181
+ return best_centers_local, best_sum_radii_local
182
+
183
+ def _global_refinement_sa(self, initial_centers):
184
+ """
185
+ Applies a global SA search with probabilistic cluster moves to refine all circles.
186
+ """
187
+ sa_iterations = 350 # Increased for more thorough search
188
+ sa_initial_temp = 5e-6
189
+ sa_cooling_rate = 0.99
190
+ sa_initial_step_size = 0.001
191
+ current_centers = np.copy(initial_centers)
192
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
193
+ current_sum_radii = np.sum(current_radii)
194
+ best_centers, best_sum_radii = np.copy(current_centers), current_sum_radii
195
+ temp, step_size = sa_initial_temp, sa_initial_step_size
196
+ all_indices = np.arange(self.n)
197
+ acceptance_window, acceptance_count = 30, 0
198
+ target_acceptance_rate, adjustment_factor = 0.44, 1.05
199
+ cluster_move_prob = 0.15
200
+ num_cluster_neighbors = 2
201
+
202
+ for i in range(sa_iterations):
203
+ trial_centers = np.copy(current_centers)
204
+
205
+ # Probabilistically choose between a single move and a cluster move
206
+ if np.random.rand() < cluster_move_prob:
207
+ # --- Cluster Move ---
208
+ inv_radii = 1.0 / (current_radii + 1e-9)
209
+ weights = (inv_radii - np.min(inv_radii))**2
210
+ primary_idx = np.random.choice(all_indices, p=weights / np.sum(weights)) if np.sum(weights) > 1e-9 else np.random.choice(all_indices)
211
+ distances = np.linalg.norm(trial_centers - trial_centers[primary_idx], axis=1)
212
+ distances[primary_idx] = np.inf
213
+ neighbor_indices = np.argsort(distances)[:num_cluster_neighbors]
214
+ cluster_indices = np.append(neighbor_indices, primary_idx)
215
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
216
+ trial_centers[cluster_indices] += move
217
+ trial_centers[cluster_indices] = np.clip(trial_centers[cluster_indices], 0.0, 1.0)
218
+ else:
219
+ # --- Single Move ---
220
+ inv_radii = 1.0 / (current_radii + 1e-9)
221
+ weights = (inv_radii - np.min(inv_radii))**2
222
+ idx_to_move = np.random.choice(all_indices, p=weights / np.sum(weights)) if np.sum(weights) > 1e-9 else np.random.choice(all_indices)
223
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
224
+ trial_centers[idx_to_move] += move
225
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move], 0.0, 1.0)
226
+
227
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
228
+ trial_sum_radii = np.sum(trial_radii)
229
+ delta_energy = trial_sum_radii - current_sum_radii
230
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
231
+ current_centers, current_radii, current_sum_radii = trial_centers, trial_radii, trial_sum_radii
232
+ acceptance_count += 1
233
+ if current_sum_radii > best_sum_radii:
234
+ best_sum_radii = current_sum_radii
235
+ best_centers = np.copy(current_centers)
236
+
237
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
238
+ step_size *= adjustment_factor if acceptance_count / acceptance_window > target_acceptance_rate else 1/adjustment_factor
239
+ acceptance_count = 0
240
+
241
+ temp *= sa_cooling_rate
242
+ step_size = max(step_size * sa_cooling_rate, 1e-8)
243
+ return best_centers
244
+
245
+ def construct_packing(self):
246
+ """
247
+ Orchestrates the multi-stage optimization process.
248
+ """
249
+ self._initial_grid_placement()
250
+ if self.n > 25:
251
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
252
+ centers_after_local_sa, _ = self._local_refinement_cluster_sa(
253
+ centers_after_search,
254
+ sum_radii_after_search
255
+ )
256
+ centers_after_global_sa = self._global_refinement_sa(centers_after_local_sa)
257
+ self.centers = centers_after_global_sa
258
+
259
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
260
+ return self.centers, self.radii
261
+
262
+
263
+ def construct_packing():
264
+ """
265
+ Constructs an arrangement of 26 circles by leveraging a superior multi-stage
266
+ optimization strategy including grid search, local SA, and global SA with cluster moves.
267
+ """
268
+ packer = CirclePacker(num_circles=26)
269
+ centers, radii = packer.construct_packing()
270
+ return centers, radii
271
+ # EVOLVE-BLOCK-END
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_103/edit.diff ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,265 +1,266 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+ from itertools import product
7
+
8
+
9
+ class CirclePacker:
10
+ """
11
+ A class to construct circle packings within a unit square using a hybrid
12
+ optimization approach. It orchestrates initial placement, exhaustive
13
+ search for a critical circle, local refinement, and iterative radius adjustment.
14
+ """
15
+ def __init__(self, num_circles=26):
16
+ self.n = num_circles
17
+ self.centers = np.zeros((self.n, 2))
18
+ self.radii = np.zeros(self.n)
19
+
20
+ @staticmethod
21
+ def _compute_max_radii_static(centers, n):
22
+ """
23
+ Computes maximum radii using an iterative method with an exponential decay
24
+ for growth factor and tolerance, providing a smoother convergence. The inner
25
+ iteration count is tuned for high precision based on successful priors.
26
+ """
27
+ radii = np.zeros(n)
28
+
29
+ # Parameters using exponential decay for smoother convergence
30
+ growth_factor_start = 1.005
31
+ growth_factor_end = 1.002
32
+ tolerance_start = 1e-7
33
+ tolerance_end = 1e-11 # Tighter tolerance
34
+
35
+ outer_iterations = 400
36
+ inner_iterations = 20 # Tuned for precision/speed balance
37
+
38
+ # Initialize radii based on boundary distance
39
+ for i in range(n):
40
+ x, y = centers[i]
41
+ radii[i] = min(x, 1 - x, y, 1 - y)
42
+
43
+ for k in range(outer_iterations):
44
+ # Use exponential interpolation for smoother parameter transition
45
+ progress = k / (outer_iterations - 1 + 1e-9)
46
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**progress
47
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**progress
48
+
49
+ radii *= current_growth_factor
50
+
51
+ for _ in range(inner_iterations):
52
+ constraints_changed = False
53
+ # Enforce boundary constraints
54
+ for i in range(n):
55
+ x, y = centers[i]
56
+ boundary_limit = min(x, 1 - x, y, 1 - y)
57
+ if radii[i] > boundary_limit + current_tolerance:
58
+ radii[i] = boundary_limit
59
+ constraints_changed = True
60
+
61
+ # Resolve overlaps between circles
62
+ for i in range(n):
63
+ for j in range(i + 1, n):
64
+ dist = np.linalg.norm(centers[i] - centers[j])
65
+ if radii[i] + radii[j] > dist + current_tolerance:
66
+ total_radius = radii[i] + radii[j]
67
+ if total_radius > 1e-12: # Avoid division by zero
68
+ scale = dist / total_radius
69
+ radii[i] *= scale
70
+ radii[j] *= scale
71
+ constraints_changed = True
72
+ if not constraints_changed:
73
+ break
74
+ return radii
75
+
76
+ def _initial_grid_placement(self):
77
+ """
78
+ Places the first 25 circles in a 5x5 grid pattern.
79
+ """
80
+ coords = np.linspace(0.1, 0.9, 5)
81
+ grid_centers = np.array(list(product(coords, coords)))
82
+ self.centers[:25] = grid_centers
83
+
84
+ def _find_optimal_26th_circle_position(self):
85
+ """
86
+ Performs a hierarchical grid search for the 26th circle by perturbing
87
+ around core interstitial points for a denser, more effective search.
88
+ """
89
+ base_25_centers = np.copy(self.centers[:25])
90
+
91
+ # Hierarchical search: 4x4 coarse grid + 3x3 fine grid around each point
92
+ interstitial_core_coords = np.linspace(0.2, 0.8, 4)
93
+ delta = 0.025
94
+ - perturbation_offsets = np.array([-delta, 0, delta])
95
+ + # Increased density for perturbation offsets: 5 points instead of 3 for a 5x5 sub-grid
96
+ + perturbation_offsets = np.linspace(-delta, delta, 5)
97
+
98
+ candidate_points = []
99
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
100
+ for offset_x, offset_y in product(perturbation_offsets, perturbation_offsets):
101
+ candidate_points.append([base_x + offset_x, base_y + offset_y])
102
+
103
+ best_sum_radii = -1.0
104
+ optimal_centers = None
105
+
106
+ for candidate_pos in candidate_points:
107
+ # Clip to ensure validity before calculation
108
+ trial_centers = np.vstack([base_25_centers, np.clip(candidate_pos, 0.0, 1.0)])
109
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
110
+ current_sum_radii = np.sum(trial_radii)
111
+
112
+ if current_sum_radii > best_sum_radii:
113
+ best_sum_radii = current_sum_radii
114
+ optimal_centers = trial_centers
115
+
116
+ if optimal_centers is None:
117
+ # Fallback
118
+ optimal_centers = np.vstack([base_25_centers, [[0.5, 0.5]]])
119
+ best_sum_radii = np.sum(CirclePacker._compute_max_radii_static(optimal_centers, self.n))
120
+
121
+ return optimal_centers, best_sum_radii
122
+
123
+ def _local_refinement_cluster(self, initial_centers, initial_sum_radii):
124
+ """
125
+ Applies a localized SA search to fine-tune the positions of the 26th circle
126
+ and its 4 closest neighbors, using more conservative parameters.
127
+ """
128
+ current_centers = np.copy(initial_centers)
129
+ current_sum_radii = initial_sum_radii
130
+
131
+ best_centers_local = np.copy(current_centers)
132
+ best_sum_radii_local = current_sum_radii
133
+
134
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
135
+ index_to_refine = 25
136
+ num_neighbors = 4
137
+ distances_to_26th = np.linalg.norm(initial_centers[index_to_refine] - initial_centers[:index_to_refine], axis=1)
138
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:num_neighbors]
139
+ indices_to_perturb = np.append(closest_neighbor_indices, index_to_refine)
140
+
141
+ # Tuned SA parameters for local cluster refinement
142
+ num_iterations = 300
143
+ initial_step_size = 0.005
144
+ initial_temp = 0.0002
145
+ cooling_rate = 0.99
146
+
147
+ step_size = initial_step_size
148
+ temp = initial_temp
149
+
150
+ for _ in range(num_iterations):
151
+ trial_centers = np.copy(current_centers)
152
+
153
+ # Perturb a random circle from the cluster
154
+ idx = np.random.choice(indices_to_perturb)
155
+
156
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
157
+ trial_centers[idx] += move
158
+ trial_centers[idx] = np.clip(trial_centers[idx], 0.0, 1.0)
159
+
160
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
161
+ trial_sum_radii = np.sum(trial_radii)
162
+
163
+ delta_energy = trial_sum_radii - current_sum_radii
164
+ if delta_energy > 0 or (temp > 1e-9 and np.random.random() < np.exp(delta_energy / temp)):
165
+ current_centers = trial_centers
166
+ current_sum_radii = trial_sum_radii
167
+
168
+ if current_sum_radii > best_sum_radii_local:
169
+ best_sum_radii_local = current_sum_radii
170
+ best_centers_local = np.copy(current_centers)
171
+
172
+ temp *= cooling_rate
173
+ step_size = max(step_size * cooling_rate, 1e-7)
174
+
175
+ return best_centers_local, best_sum_radii_local
176
+
177
+ def _global_refinement_sa(self, initial_centers, initial_sum_radii):
178
+ """
179
+ Applies a global, low-temperature SA to "jiggle" all circles into a
180
+ better global optimum, using tuned parameters from high-scoring versions.
181
+ """
182
+ current_centers = np.copy(initial_centers)
183
+ current_sum_radii = initial_sum_radii
184
+
185
+ best_centers_global = np.copy(current_centers)
186
+ best_sum_radii_global = current_sum_radii
187
+
188
+ # SA parameters for a final, gentle, global refinement
189
+ num_iterations = 1500
190
+ initial_step_size = 0.004
191
+ initial_temp = 1e-5
192
+ cooling_rate = 0.995
193
+
194
+ step_size = initial_step_size
195
+ temp = initial_temp
196
+
197
+ for _ in range(num_iterations):
198
+ # Perturb a random circle from the entire set
199
+ idx_to_perturb = np.random.randint(self.n)
200
+
201
+ trial_centers = np.copy(current_centers)
202
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
203
+ trial_centers[idx_to_perturb] += move
204
+ trial_centers[idx_to_perturb] = np.clip(trial_centers[idx_to_perturb], 0.0, 1.0)
205
+
206
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
207
+ trial_sum_radii = np.sum(trial_radii)
208
+
209
+ delta_energy = trial_sum_radii - current_sum_radii
210
+ if delta_energy > 0 or (temp > 1e-9 and np.random.random() < np.exp(delta_energy / temp)):
211
+ current_centers = trial_centers
212
+ current_sum_radii = trial_sum_radii
213
+
214
+ if current_sum_radii > best_sum_radii_global:
215
+ best_sum_radii_global = current_sum_radii
216
+ best_centers_global = np.copy(current_centers)
217
+
218
+ temp *= cooling_rate
219
+ step_size = max(step_size * cooling_rate, 5e-8)
220
+
221
+ return best_centers_global, best_sum_radii_global
222
+
223
+ def construct_packing(self):
224
+ """
225
+ Main method using a multi-stage funnel: exhaustive search -> local SA -> global SA.
226
+ """
227
+ np.random.seed(42) # For reproducible SA results
228
+ self._initial_grid_placement()
229
+
230
+ if self.n > 25:
231
+ # Stage 1: Denser exhaustive search for the optimal 26th circle position.
232
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
233
+
234
+ # Stage 2: Local refinement on the 26th circle and its neighbors.
235
+ centers_after_local, sum_radii_after_local = self._local_refinement_cluster(
236
+ centers_after_search,
237
+ sum_radii_after_search
238
+ )
239
+
240
+ # Stage 3: Global "gentle jiggle" refinement using SA on all circles.
241
+ refined_centers, _ = self._global_refinement_sa(
242
+ centers_after_local,
243
+ sum_radii_after_local
244
+ )
245
+ self.centers = refined_centers
246
+
247
+ # Final radius calculation for the fully optimized center configuration.
248
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
249
+ return self.centers, self.radii
250
+
251
+ def construct_packing():
252
+ """
253
+ Constructs an arrangement of 26 circles by leveraging a modular,
254
+ multi-stage optimization strategy to maximize the sum of radii.
255
+ """
256
+ packer = CirclePacker(num_circles=26)
257
+ centers, radii = packer.construct_packing()
258
+ return centers, radii
259
+
260
+
261
+ # EVOLVE-BLOCK-END
262
+
263
+
264
+ # This part remains fixed (not evolved)
265
+ def run_packing():
266
+ """Run the circle packing constructor for n=26"""
267
+ centers, radii = construct_packing()
268
+ # Calculate the sum of radii
269
+ sum_radii = np.sum(radii)
270
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_103/main.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings within a unit square using a hybrid
9
+ optimization approach. It orchestrates initial placement, exhaustive
10
+ search for a critical circle, local refinement, and iterative radius adjustment.
11
+ """
12
+ def __init__(self, num_circles=26):
13
+ self.n = num_circles
14
+ self.centers = np.zeros((self.n, 2))
15
+ self.radii = np.zeros(self.n)
16
+
17
+ @staticmethod
18
+ def _compute_max_radii_static(centers, n):
19
+ """
20
+ Computes maximum radii using an iterative method with an exponential decay
21
+ for growth factor and tolerance, providing a smoother convergence. The inner
22
+ iteration count is tuned for high precision based on successful priors.
23
+ """
24
+ radii = np.zeros(n)
25
+
26
+ # Parameters using exponential decay for smoother convergence
27
+ growth_factor_start = 1.005
28
+ growth_factor_end = 1.002
29
+ tolerance_start = 1e-7
30
+ tolerance_end = 1e-11 # Tighter tolerance
31
+
32
+ outer_iterations = 400
33
+ inner_iterations = 20 # Tuned for precision/speed balance
34
+
35
+ # Initialize radii based on boundary distance
36
+ for i in range(n):
37
+ x, y = centers[i]
38
+ radii[i] = min(x, 1 - x, y, 1 - y)
39
+
40
+ for k in range(outer_iterations):
41
+ # Use exponential interpolation for smoother parameter transition
42
+ progress = k / (outer_iterations - 1 + 1e-9)
43
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**progress
44
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**progress
45
+
46
+ radii *= current_growth_factor
47
+
48
+ for _ in range(inner_iterations):
49
+ constraints_changed = False
50
+ # Enforce boundary constraints
51
+ for i in range(n):
52
+ x, y = centers[i]
53
+ boundary_limit = min(x, 1 - x, y, 1 - y)
54
+ if radii[i] > boundary_limit + current_tolerance:
55
+ radii[i] = boundary_limit
56
+ constraints_changed = True
57
+
58
+ # Resolve overlaps between circles
59
+ for i in range(n):
60
+ for j in range(i + 1, n):
61
+ dist = np.linalg.norm(centers[i] - centers[j])
62
+ if radii[i] + radii[j] > dist + current_tolerance:
63
+ total_radius = radii[i] + radii[j]
64
+ if total_radius > 1e-12: # Avoid division by zero
65
+ scale = dist / total_radius
66
+ radii[i] *= scale
67
+ radii[j] *= scale
68
+ constraints_changed = True
69
+ if not constraints_changed:
70
+ break
71
+ return radii
72
+
73
+ def _initial_grid_placement(self):
74
+ """
75
+ Places the first 25 circles in a 5x5 grid pattern.
76
+ """
77
+ coords = np.linspace(0.1, 0.9, 5)
78
+ grid_centers = np.array(list(product(coords, coords)))
79
+ self.centers[:25] = grid_centers
80
+
81
+ def _find_optimal_26th_circle_position(self):
82
+ """
83
+ Performs a hierarchical grid search for the 26th circle by perturbing
84
+ around core interstitial points for a denser, more effective search.
85
+ """
86
+ base_25_centers = np.copy(self.centers[:25])
87
+
88
+ # Hierarchical search: 4x4 coarse grid + 3x3 fine grid around each point
89
+ interstitial_core_coords = np.linspace(0.2, 0.8, 4)
90
+ delta = 0.025
91
+ # Increased density for perturbation offsets: 5 points instead of 3 for a 5x5 sub-grid
92
+ perturbation_offsets = np.linspace(-delta, delta, 5)
93
+
94
+ candidate_points = []
95
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
96
+ for offset_x, offset_y in product(perturbation_offsets, perturbation_offsets):
97
+ candidate_points.append([base_x + offset_x, base_y + offset_y])
98
+
99
+ best_sum_radii = -1.0
100
+ optimal_centers = None
101
+
102
+ for candidate_pos in candidate_points:
103
+ # Clip to ensure validity before calculation
104
+ trial_centers = np.vstack([base_25_centers, np.clip(candidate_pos, 0.0, 1.0)])
105
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
106
+ current_sum_radii = np.sum(trial_radii)
107
+
108
+ if current_sum_radii > best_sum_radii:
109
+ best_sum_radii = current_sum_radii
110
+ optimal_centers = trial_centers
111
+
112
+ if optimal_centers is None:
113
+ # Fallback
114
+ optimal_centers = np.vstack([base_25_centers, [[0.5, 0.5]]])
115
+ best_sum_radii = np.sum(CirclePacker._compute_max_radii_static(optimal_centers, self.n))
116
+
117
+ return optimal_centers, best_sum_radii
118
+
119
+ def _local_refinement_cluster(self, initial_centers, initial_sum_radii):
120
+ """
121
+ Applies a localized SA search to fine-tune the positions of the 26th circle
122
+ and its 4 closest neighbors, using more conservative parameters.
123
+ """
124
+ current_centers = np.copy(initial_centers)
125
+ current_sum_radii = initial_sum_radii
126
+
127
+ best_centers_local = np.copy(current_centers)
128
+ best_sum_radii_local = current_sum_radii
129
+
130
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
131
+ index_to_refine = 25
132
+ num_neighbors = 4
133
+ distances_to_26th = np.linalg.norm(initial_centers[index_to_refine] - initial_centers[:index_to_refine], axis=1)
134
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:num_neighbors]
135
+ indices_to_perturb = np.append(closest_neighbor_indices, index_to_refine)
136
+
137
+ # Tuned SA parameters for local cluster refinement
138
+ num_iterations = 300
139
+ initial_step_size = 0.005
140
+ initial_temp = 0.0002
141
+ cooling_rate = 0.99
142
+
143
+ step_size = initial_step_size
144
+ temp = initial_temp
145
+
146
+ for _ in range(num_iterations):
147
+ trial_centers = np.copy(current_centers)
148
+
149
+ # Perturb a random circle from the cluster
150
+ idx = np.random.choice(indices_to_perturb)
151
+
152
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
153
+ trial_centers[idx] += move
154
+ trial_centers[idx] = np.clip(trial_centers[idx], 0.0, 1.0)
155
+
156
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
157
+ trial_sum_radii = np.sum(trial_radii)
158
+
159
+ delta_energy = trial_sum_radii - current_sum_radii
160
+ if delta_energy > 0 or (temp > 1e-9 and np.random.random() < np.exp(delta_energy / temp)):
161
+ current_centers = trial_centers
162
+ current_sum_radii = trial_sum_radii
163
+
164
+ if current_sum_radii > best_sum_radii_local:
165
+ best_sum_radii_local = current_sum_radii
166
+ best_centers_local = np.copy(current_centers)
167
+
168
+ temp *= cooling_rate
169
+ step_size = max(step_size * cooling_rate, 1e-7)
170
+
171
+ return best_centers_local, best_sum_radii_local
172
+
173
+ def _global_refinement_sa(self, initial_centers, initial_sum_radii):
174
+ """
175
+ Applies a global, low-temperature SA to "jiggle" all circles into a
176
+ better global optimum, using tuned parameters from high-scoring versions.
177
+ """
178
+ current_centers = np.copy(initial_centers)
179
+ current_sum_radii = initial_sum_radii
180
+
181
+ best_centers_global = np.copy(current_centers)
182
+ best_sum_radii_global = current_sum_radii
183
+
184
+ # SA parameters for a final, gentle, global refinement
185
+ num_iterations = 1500
186
+ initial_step_size = 0.004
187
+ initial_temp = 1e-5
188
+ cooling_rate = 0.995
189
+
190
+ step_size = initial_step_size
191
+ temp = initial_temp
192
+
193
+ for _ in range(num_iterations):
194
+ # Perturb a random circle from the entire set
195
+ idx_to_perturb = np.random.randint(self.n)
196
+
197
+ trial_centers = np.copy(current_centers)
198
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
199
+ trial_centers[idx_to_perturb] += move
200
+ trial_centers[idx_to_perturb] = np.clip(trial_centers[idx_to_perturb], 0.0, 1.0)
201
+
202
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
203
+ trial_sum_radii = np.sum(trial_radii)
204
+
205
+ delta_energy = trial_sum_radii - current_sum_radii
206
+ if delta_energy > 0 or (temp > 1e-9 and np.random.random() < np.exp(delta_energy / temp)):
207
+ current_centers = trial_centers
208
+ current_sum_radii = trial_sum_radii
209
+
210
+ if current_sum_radii > best_sum_radii_global:
211
+ best_sum_radii_global = current_sum_radii
212
+ best_centers_global = np.copy(current_centers)
213
+
214
+ temp *= cooling_rate
215
+ step_size = max(step_size * cooling_rate, 5e-8)
216
+
217
+ return best_centers_global, best_sum_radii_global
218
+
219
+ def construct_packing(self):
220
+ """
221
+ Main method using a multi-stage funnel: exhaustive search -> local SA -> global SA.
222
+ """
223
+ np.random.seed(42) # For reproducible SA results
224
+ self._initial_grid_placement()
225
+
226
+ if self.n > 25:
227
+ # Stage 1: Denser exhaustive search for the optimal 26th circle position.
228
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
229
+
230
+ # Stage 2: Local refinement on the 26th circle and its neighbors.
231
+ centers_after_local, sum_radii_after_local = self._local_refinement_cluster(
232
+ centers_after_search,
233
+ sum_radii_after_search
234
+ )
235
+
236
+ # Stage 3: Global "gentle jiggle" refinement using SA on all circles.
237
+ refined_centers, _ = self._global_refinement_sa(
238
+ centers_after_local,
239
+ sum_radii_after_local
240
+ )
241
+ self.centers = refined_centers
242
+
243
+ # Final radius calculation for the fully optimized center configuration.
244
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
245
+ return self.centers, self.radii
246
+
247
+ def construct_packing():
248
+ """
249
+ Constructs an arrangement of 26 circles by leveraging a modular,
250
+ multi-stage optimization strategy to maximize the sum of radii.
251
+ """
252
+ packer = CirclePacker(num_circles=26)
253
+ centers, radii = packer.construct_packing()
254
+ return centers, radii
255
+
256
+
257
+ # EVOLVE-BLOCK-END
258
+
259
+
260
+ # This part remains fixed (not evolved)
261
+ def run_packing():
262
+ """Run the circle packing constructor for n=26"""
263
+ centers, radii = construct_packing()
264
+ # Calculate the sum of radii
265
+ sum_radii = np.sum(radii)
266
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_103/original.py ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings within a unit square using a hybrid
9
+ optimization approach. It orchestrates initial placement, exhaustive
10
+ search for a critical circle, local refinement, and iterative radius adjustment.
11
+ """
12
+ def __init__(self, num_circles=26):
13
+ self.n = num_circles
14
+ self.centers = np.zeros((self.n, 2))
15
+ self.radii = np.zeros(self.n)
16
+
17
+ @staticmethod
18
+ def _compute_max_radii_static(centers, n):
19
+ """
20
+ Computes maximum radii using an iterative method with an exponential decay
21
+ for growth factor and tolerance, providing a smoother convergence. The inner
22
+ iteration count is tuned for high precision based on successful priors.
23
+ """
24
+ radii = np.zeros(n)
25
+
26
+ # Parameters using exponential decay for smoother convergence
27
+ growth_factor_start = 1.005
28
+ growth_factor_end = 1.002
29
+ tolerance_start = 1e-7
30
+ tolerance_end = 1e-11 # Tighter tolerance
31
+
32
+ outer_iterations = 400
33
+ inner_iterations = 20 # Tuned for precision/speed balance
34
+
35
+ # Initialize radii based on boundary distance
36
+ for i in range(n):
37
+ x, y = centers[i]
38
+ radii[i] = min(x, 1 - x, y, 1 - y)
39
+
40
+ for k in range(outer_iterations):
41
+ # Use exponential interpolation for smoother parameter transition
42
+ progress = k / (outer_iterations - 1 + 1e-9)
43
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**progress
44
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**progress
45
+
46
+ radii *= current_growth_factor
47
+
48
+ for _ in range(inner_iterations):
49
+ constraints_changed = False
50
+ # Enforce boundary constraints
51
+ for i in range(n):
52
+ x, y = centers[i]
53
+ boundary_limit = min(x, 1 - x, y, 1 - y)
54
+ if radii[i] > boundary_limit + current_tolerance:
55
+ radii[i] = boundary_limit
56
+ constraints_changed = True
57
+
58
+ # Resolve overlaps between circles
59
+ for i in range(n):
60
+ for j in range(i + 1, n):
61
+ dist = np.linalg.norm(centers[i] - centers[j])
62
+ if radii[i] + radii[j] > dist + current_tolerance:
63
+ total_radius = radii[i] + radii[j]
64
+ if total_radius > 1e-12: # Avoid division by zero
65
+ scale = dist / total_radius
66
+ radii[i] *= scale
67
+ radii[j] *= scale
68
+ constraints_changed = True
69
+ if not constraints_changed:
70
+ break
71
+ return radii
72
+
73
+ def _initial_grid_placement(self):
74
+ """
75
+ Places the first 25 circles in a 5x5 grid pattern.
76
+ """
77
+ coords = np.linspace(0.1, 0.9, 5)
78
+ grid_centers = np.array(list(product(coords, coords)))
79
+ self.centers[:25] = grid_centers
80
+
81
+ def _find_optimal_26th_circle_position(self):
82
+ """
83
+ Performs a hierarchical grid search for the 26th circle by perturbing
84
+ around core interstitial points for a denser, more effective search.
85
+ """
86
+ base_25_centers = np.copy(self.centers[:25])
87
+
88
+ # Hierarchical search: 4x4 coarse grid + 3x3 fine grid around each point
89
+ interstitial_core_coords = np.linspace(0.2, 0.8, 4)
90
+ delta = 0.025
91
+ perturbation_offsets = np.array([-delta, 0, delta])
92
+
93
+ candidate_points = []
94
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
95
+ for offset_x, offset_y in product(perturbation_offsets, perturbation_offsets):
96
+ candidate_points.append([base_x + offset_x, base_y + offset_y])
97
+
98
+ best_sum_radii = -1.0
99
+ optimal_centers = None
100
+
101
+ for candidate_pos in candidate_points:
102
+ # Clip to ensure validity before calculation
103
+ trial_centers = np.vstack([base_25_centers, np.clip(candidate_pos, 0.0, 1.0)])
104
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
105
+ current_sum_radii = np.sum(trial_radii)
106
+
107
+ if current_sum_radii > best_sum_radii:
108
+ best_sum_radii = current_sum_radii
109
+ optimal_centers = trial_centers
110
+
111
+ if optimal_centers is None:
112
+ # Fallback
113
+ optimal_centers = np.vstack([base_25_centers, [[0.5, 0.5]]])
114
+ best_sum_radii = np.sum(CirclePacker._compute_max_radii_static(optimal_centers, self.n))
115
+
116
+ return optimal_centers, best_sum_radii
117
+
118
+ def _local_refinement_cluster(self, initial_centers, initial_sum_radii):
119
+ """
120
+ Applies a localized SA search to fine-tune the positions of the 26th circle
121
+ and its 4 closest neighbors, using more conservative parameters.
122
+ """
123
+ current_centers = np.copy(initial_centers)
124
+ current_sum_radii = initial_sum_radii
125
+
126
+ best_centers_local = np.copy(current_centers)
127
+ best_sum_radii_local = current_sum_radii
128
+
129
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
130
+ index_to_refine = 25
131
+ num_neighbors = 4
132
+ distances_to_26th = np.linalg.norm(initial_centers[index_to_refine] - initial_centers[:index_to_refine], axis=1)
133
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:num_neighbors]
134
+ indices_to_perturb = np.append(closest_neighbor_indices, index_to_refine)
135
+
136
+ # Tuned SA parameters for local cluster refinement
137
+ num_iterations = 300
138
+ initial_step_size = 0.005
139
+ initial_temp = 0.0002
140
+ cooling_rate = 0.99
141
+
142
+ step_size = initial_step_size
143
+ temp = initial_temp
144
+
145
+ for _ in range(num_iterations):
146
+ trial_centers = np.copy(current_centers)
147
+
148
+ # Perturb a random circle from the cluster
149
+ idx = np.random.choice(indices_to_perturb)
150
+
151
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
152
+ trial_centers[idx] += move
153
+ trial_centers[idx] = np.clip(trial_centers[idx], 0.0, 1.0)
154
+
155
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
156
+ trial_sum_radii = np.sum(trial_radii)
157
+
158
+ delta_energy = trial_sum_radii - current_sum_radii
159
+ if delta_energy > 0 or (temp > 1e-9 and np.random.random() < np.exp(delta_energy / temp)):
160
+ current_centers = trial_centers
161
+ current_sum_radii = trial_sum_radii
162
+
163
+ if current_sum_radii > best_sum_radii_local:
164
+ best_sum_radii_local = current_sum_radii
165
+ best_centers_local = np.copy(current_centers)
166
+
167
+ temp *= cooling_rate
168
+ step_size = max(step_size * cooling_rate, 1e-7)
169
+
170
+ return best_centers_local, best_sum_radii_local
171
+
172
+ def _global_refinement_sa(self, initial_centers, initial_sum_radii):
173
+ """
174
+ Applies a global, low-temperature SA to "jiggle" all circles into a
175
+ better global optimum, using tuned parameters from high-scoring versions.
176
+ """
177
+ current_centers = np.copy(initial_centers)
178
+ current_sum_radii = initial_sum_radii
179
+
180
+ best_centers_global = np.copy(current_centers)
181
+ best_sum_radii_global = current_sum_radii
182
+
183
+ # SA parameters for a final, gentle, global refinement
184
+ num_iterations = 1500
185
+ initial_step_size = 0.004
186
+ initial_temp = 1e-5
187
+ cooling_rate = 0.995
188
+
189
+ step_size = initial_step_size
190
+ temp = initial_temp
191
+
192
+ for _ in range(num_iterations):
193
+ # Perturb a random circle from the entire set
194
+ idx_to_perturb = np.random.randint(self.n)
195
+
196
+ trial_centers = np.copy(current_centers)
197
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
198
+ trial_centers[idx_to_perturb] += move
199
+ trial_centers[idx_to_perturb] = np.clip(trial_centers[idx_to_perturb], 0.0, 1.0)
200
+
201
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
202
+ trial_sum_radii = np.sum(trial_radii)
203
+
204
+ delta_energy = trial_sum_radii - current_sum_radii
205
+ if delta_energy > 0 or (temp > 1e-9 and np.random.random() < np.exp(delta_energy / temp)):
206
+ current_centers = trial_centers
207
+ current_sum_radii = trial_sum_radii
208
+
209
+ if current_sum_radii > best_sum_radii_global:
210
+ best_sum_radii_global = current_sum_radii
211
+ best_centers_global = np.copy(current_centers)
212
+
213
+ temp *= cooling_rate
214
+ step_size = max(step_size * cooling_rate, 5e-8)
215
+
216
+ return best_centers_global, best_sum_radii_global
217
+
218
+ def construct_packing(self):
219
+ """
220
+ Main method using a multi-stage funnel: exhaustive search -> local SA -> global SA.
221
+ """
222
+ np.random.seed(42) # For reproducible SA results
223
+ self._initial_grid_placement()
224
+
225
+ if self.n > 25:
226
+ # Stage 1: Denser exhaustive search for the optimal 26th circle position.
227
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
228
+
229
+ # Stage 2: Local refinement on the 26th circle and its neighbors.
230
+ centers_after_local, sum_radii_after_local = self._local_refinement_cluster(
231
+ centers_after_search,
232
+ sum_radii_after_search
233
+ )
234
+
235
+ # Stage 3: Global "gentle jiggle" refinement using SA on all circles.
236
+ refined_centers, _ = self._global_refinement_sa(
237
+ centers_after_local,
238
+ sum_radii_after_local
239
+ )
240
+ self.centers = refined_centers
241
+
242
+ # Final radius calculation for the fully optimized center configuration.
243
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
244
+ return self.centers, self.radii
245
+
246
+ def construct_packing():
247
+ """
248
+ Constructs an arrangement of 26 circles by leveraging a modular,
249
+ multi-stage optimization strategy to maximize the sum of radii.
250
+ """
251
+ packer = CirclePacker(num_circles=26)
252
+ centers, radii = packer.construct_packing()
253
+ return centers, radii
254
+
255
+
256
+ # EVOLVE-BLOCK-END
257
+
258
+
259
+ # This part remains fixed (not evolved)
260
+ def run_packing():
261
+ """Run the circle packing constructor for n=26"""
262
+ centers, radii = construct_packing()
263
+ # Calculate the sum of radii
264
+ sum_radii = np.sum(radii)
265
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_103/search_replace.txt ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <NAME>
2
+ denser_26th_circle_search
3
+ </NAME>
4
+
5
+ <DESCRIPTION>
6
+ Refine the hierarchical search for the 26th circle by increasing the density of perturbation offsets.
7
+ The original code used 3 perturbation offsets (`-delta`, `0`, `delta`), leading to a 3x3 perturbation grid (9 points) around each of the 16 interstitial core coordinates.
8
+ This modification changes the perturbation offsets to a `np.linspace(-delta, delta, 5)`, creating a 5x5 perturbation grid (25 points) around each core coordinate.
9
+ This increases the total candidate positions for the 26th circle from 144 to 400, allowing for a more granular and potentially more optimal initial placement, which can significantly benefit the subsequent SA stages.
10
+ This change directly addresses "Recommendation 1: Refine the Hierarchical Interstitial Search for the 26th Circle with Increased Density" from the provided actionable recommendations.
11
+ </DESCRIPTION>
12
+
13
+ <DIFF>
14
+ <<<<<<< SEARCH
15
+ delta = 0.025
16
+ perturbation_offsets = np.array([-delta, 0, delta])
17
+
18
+ candidate_points = []
19
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
20
+ for offset_x, offset_y in product(perturbation_offsets, perturbation_offsets):
21
+ candidate_points.append([base_x + offset_x, base_y + offset_y])
22
+ =======
23
+ delta = 0.025
24
+ # Increased density for perturbation offsets: 5 points instead of 3 for a 5x5 sub-grid
25
+ perturbation_offsets = np.linspace(-delta, delta, 5)
26
+
27
+ candidate_points = []
28
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
29
+ for offset_x, offset_y in product(perturbation_offsets, perturbation_offsets):
30
+ candidate_points.append([base_x + offset_x, base_y + offset_y])
31
+ >>>>>>> REPLACE
32
+ </DIFF>
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_104/edit.diff ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,252 +1,266 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+ from itertools import product
7
+
8
+
9
+ class CirclePacker:
10
+ """
11
+ A class to construct circle packings using a multi-stage optimization process:
12
+ 1. Initial 5x5 grid placement.
13
+ 2. A refined interstitial grid search for the 26th circle.
14
+ 3. A localized Simulated Annealing (SA) refinement of the resulting cluster.
15
+ + 4. An extended global SA refinement for the entire packing.
16
+ """
17
+ def __init__(self, num_circles=26):
18
+ """Initializes the packer for 26 circles."""
19
+ if num_circles != 26:
20
+ raise ValueError("This CirclePacker is specialized for exactly 26 circles.")
21
+ self.n = num_circles
22
+ self.centers = np.zeros((self.n, 2))
23
+ self.radii = np.zeros(self.n)
24
+
25
+ @staticmethod
26
+ - def _compute_max_radii_static(centers: np.ndarray) -> np.ndarray:
27
+ + def _compute_max_radii_static(centers: np.ndarray, n: int) -> np.ndarray:
28
+ """
29
+ Computes maximum radii using an iterative method with an adaptive growth
30
+ factor and tolerance with exponential decay, adopted from the highest-scoring
31
+ prior implementations for superior performance.
32
+
33
+ Args:
34
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
35
+ + n (int): Number of circles (passed explicitly for consistency with some priors).
36
+
37
+ Returns:
38
+ np.array of shape (n) with the final radius of each circle.
39
+ """
40
+ - n = centers.shape[0]
41
+ radii = np.zeros(n)
42
+
43
+ - # Parameters from the high-scoring prior program (score 2.5406)
44
+ + # Parameters from high-scoring prior programs (score 2.5407)
45
+ growth_factor_start = 1.005
46
+ growth_factor_end = 1.002
47
+ outer_iterations = 400
48
+ tolerance_start = 1e-7
49
+ - tolerance_end = 1e-11
50
+ - # Increased inner iterations for more robust constraint satisfaction
51
+ - inner_iterations = 20
52
+ + tolerance_end = 1e-11 # Tighter tolerance for precision
53
+ + inner_iterations = 20 # Increased for more robust constraint satisfaction
54
+
55
+ # Initialize radii based on boundary distance
56
+ for i in range(n):
57
+ x, y = centers[i]
58
+ radii[i] = min(x, 1 - x, y, 1 - y)
59
+
60
+ # Iteratively grow and resolve constraints
61
+ for k in range(outer_iterations):
62
+ progress = k / (outer_iterations - 1 + 1e-9)
63
+ # Exponential interpolation for smooth parameter transition
64
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**progress
65
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**progress
66
+
67
+ radii *= current_growth_factor
68
+
69
+ for _ in range(inner_iterations):
70
+ constraints_changed = False
71
+ # Boundary constraints
72
+ for i in range(n):
73
+ x, y = centers[i]
74
+ boundary_limit = min(x, 1 - x, y, 1 - y)
75
+ if radii[i] > boundary_limit + current_tolerance:
76
+ radii[i] = boundary_limit
77
+ constraints_changed = True
78
+
79
+ # Overlap constraints
80
+ for i in range(n):
81
+ for j in range(i + 1, n):
82
+ dist = np.linalg.norm(centers[i] - centers[j])
83
+ if radii[i] + radii[j] > dist + current_tolerance:
84
+ total_radius = radii[i] + radii[j]
85
+ - if total_radius > 1e-12: # Avoid division by zero
86
+ + if total_radius > 1e-12: # Avoid division by zero with a robust threshold
87
+ scale = dist / total_radius
88
+ radii[i] *= scale
89
+ radii[j] *= scale
90
+ constraints_changed = True
91
+ if not constraints_changed:
92
+ break
93
+ return radii
94
+
95
+ def _initial_grid_placement(self) -> np.ndarray:
96
+ """Places the first 25 circles in a perfect 5x5 grid."""
97
+ coords = np.linspace(0.1, 0.9, 5)
98
+ return np.array(list(product(coords, coords)))
99
+
100
+ def _grid_search_for_26th_circle(self, base_centers: np.ndarray) -> tuple[np.ndarray, float]:
101
+ """
102
+ Performs a refined grid search for the 26th circle by perturbing around core interstitial points.
103
+ """
104
+ interstitial_core_coords = np.linspace(0.2, 0.8, 4)
105
+ delta = 0.025
106
+ perturbation_offsets = np.array([-delta, 0, delta])
107
+
108
+ candidate_points = []
109
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
110
+ for offset_x, offset_y in product(perturbation_offsets, perturbation_offsets):
111
+ candidate_points.append([base_x + offset_x, base_y + offset_y])
112
+
113
+ best_sum_radii = -1.0
114
+ best_centers_config = None
115
+
116
+ for candidate_pos in candidate_points:
117
+ trial_centers = np.vstack([base_centers, np.clip(candidate_pos, 0.0, 1.0)])
118
+ - trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
119
+ + trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
120
+ current_sum_radii = np.sum(trial_radii)
121
+
122
+ if current_sum_radii > best_sum_radii:
123
+ best_sum_radii = current_sum_radii
124
+ best_centers_config = trial_centers
125
+
126
+ + # Fallback if no valid candidate found (shouldn't happen with proper candidate generation)
127
+ + if best_centers_config is None:
128
+ + best_centers_config = np.vstack([base_centers, [[0.5, 0.5]]])
129
+ + best_sum_radii = np.sum(CirclePacker._compute_max_radii_static(best_centers_config, self.n))
130
+ +
131
+ +
132
+ return best_centers_config, best_sum_radii
133
+
134
+ def _local_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
135
+ """
136
+ Applies a localized SA search to fine-tune the positions of the 26th circle and its 4 nearest neighbors.
137
+ """
138
+ - # Tuned SA parameters for local cluster refinement
139
+ + # Tuned SA parameters for local cluster refinement (from current high-scoring program)
140
+ sa_iterations = 300
141
+ sa_initial_temp = 0.0002
142
+ sa_cooling_rate = 0.99
143
+ sa_initial_step_size = 0.005
144
+
145
+ current_centers = np.copy(initial_centers)
146
+ current_sum_radii = initial_sum_radii
147
+
148
+ best_centers = np.copy(current_centers)
149
+ best_sum_radii = current_sum_radii
150
+
151
+ temp = sa_initial_temp
152
+ step_size = sa_initial_step_size
153
+
154
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
155
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
156
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
157
+ cluster_indices = np.append(closest_neighbor_indices, 25)
158
+
159
+ for _ in range(sa_iterations):
160
+ idx_to_move = np.random.choice(cluster_indices)
161
+
162
+ trial_centers = np.copy(current_centers)
163
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
164
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
165
+
166
+ - trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
167
+ + trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
168
+ trial_sum_radii = np.sum(trial_radii)
169
+
170
+ delta_energy = trial_sum_radii - current_sum_radii
171
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
172
+ current_centers = trial_centers
173
+ current_sum_radii = trial_sum_radii
174
+
175
+ if current_sum_radii > best_sum_radii:
176
+ best_sum_radii = current_sum_radii
177
+ best_centers = np.copy(current_centers)
178
+
179
+ temp *= sa_cooling_rate
180
+ step_size = max(step_size * sa_cooling_rate, 1e-7)
181
+
182
+ return best_centers, best_sum_radii
183
+
184
+ def _global_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
185
+ """
186
+ - Applies a global, low-temperature SA to "jiggle" all circles into a better global optimum.
187
+ - """
188
+ - # SA parameters for a final, gentle, global refinement
189
+ - sa_iterations = 1500
190
+ - sa_initial_temp = 1e-5
191
+ - sa_cooling_rate = 0.995
192
+ - sa_initial_step_size = 0.004
193
+ + Applies a global SA to "jiggle" all circles into a better global optimum,
194
+ + using parameters from the crossover inspiration for broader exploration.
195
+ + """
196
+ + # SA parameters for a final, gentle, global refinement (from crossover inspiration)
197
+ + sa_iterations = 2000 # Increased iterations
198
+ + sa_initial_temp = 0.0005 # Higher initial temperature
199
+ + sa_cooling_rate = 0.997 # Slower cooling rate
200
+ + sa_initial_step_size = 0.01 # Larger initial step size
201
+
202
+ current_centers = np.copy(initial_centers)
203
+ current_sum_radii = initial_sum_radii
204
+
205
+ best_centers = np.copy(current_centers)
206
+ best_sum_radii = current_sum_radii
207
+
208
+ temp = sa_initial_temp
209
+ step_size = sa_initial_step_size
210
+
211
+ - for _ in range(sa_iterations):
212
+ + for k in range(sa_iterations):
213
+ + # Dynamic temperature and step size schedules
214
+ + temp = sa_initial_temp * (sa_cooling_rate ** k)
215
+ + # Linearly decreasing step size for a more controlled search towards the end
216
+ + step_size = sa_initial_step_size * (1.0 - k / sa_iterations)
217
+ +
218
+ # Perturb a random circle from the entire set
219
+ idx_to_move = np.random.randint(self.n)
220
+
221
+ trial_centers = np.copy(current_centers)
222
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
223
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
224
+
225
+ - trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
226
+ + trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
227
+ trial_sum_radii = np.sum(trial_radii)
228
+
229
+ delta_energy = trial_sum_radii - current_sum_radii
230
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
231
+ current_centers = trial_centers
232
+ current_sum_radii = trial_sum_radii
233
+
234
+ if current_sum_radii > best_sum_radii:
235
+ best_sum_radii = current_sum_radii
236
+ best_centers = np.copy(current_centers)
237
+
238
+ - temp *= sa_cooling_rate
239
+ - step_size = max(step_size * sa_cooling_rate, 5e-8)
240
+ + # Ensure step_size doesn't go below a useful minimum
241
+ + step_size = max(step_size, 5e-8)
242
+
243
+ return best_centers, best_sum_radii
244
+
245
+ def construct_packing(self):
246
+ """
247
+ Orchestrates the multi-stage packing process:
248
+ 1. Grid search for 26th circle.
249
+ 2. Local SA refinement of the resulting cluster.
250
+ 3. Global SA refinement of the entire packing.
251
+ """
252
+ + np.random.seed(42) # For reproducible SA results
253
+ +
254
+ base_centers = self._initial_grid_placement()
255
+
256
+ - # Stage 1: Exhaustive search for a strong starting point
257
+ + # Stage 1: Exhaustive search for a strong starting point for the 26th circle
258
+ centers1, sum_radii1 = self._grid_search_for_26th_circle(base_centers)
259
+
260
+ - # Stage 2: Local refinement on the cluster to fine-tune
261
+ + # Stage 2: Local refinement on the cluster (26th circle + neighbors) to fine-tune
262
+ centers2, sum_radii2 = self._local_refinement_sa(centers1, sum_radii1)
263
+
264
+ - # Stage 3: Global "gentle jiggle" refinement on all circles
265
+ + # Stage 3: Global "gentle jiggle" refinement on all circles with enhanced parameters
266
+ centers3, _ = self._global_refinement_sa(centers2, sum_radii2)
267
+
268
+ self.centers = centers3
269
+ # Final radius calculation for maximum precision on the best-found centers
270
+ - self.radii = CirclePacker._compute_max_radii_static(self.centers)
271
+ + self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
272
+
273
+ return self.centers, self.radii
274
+
275
+
276
+ def construct_packing():
277
+ """
278
+ Constructs an arrangement of 26 circles by leveraging a multi-stage
279
+ optimization strategy managed by the CirclePacker class. This involves
280
+ - a refined grid search followed by Simulated Annealing for local refinement.
281
+ + a refined grid search followed by localized and then global Simulated Annealing.
282
+ """
283
+ packer = CirclePacker(num_circles=26)
284
+ centers, radii = packer.construct_packing()
285
+ return centers, radii
286
+ # EVOLVE-BLOCK-END
287
+
288
+
289
+ # This part remains fixed (not evolved)
290
+ def run_packing():
291
+ """Run the circle packing constructor for n=26"""
292
+ centers, radii = construct_packing()
293
+ # Calculate the sum of radii
294
+ sum_radii = np.sum(radii)
295
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_104/main.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings using a multi-stage optimization process:
9
+ 1. Initial 5x5 grid placement.
10
+ 2. A refined interstitial grid search for the 26th circle.
11
+ 3. A localized Simulated Annealing (SA) refinement of the resulting cluster.
12
+ 4. An extended global SA refinement for the entire packing.
13
+ """
14
+ def __init__(self, num_circles=26):
15
+ """Initializes the packer for 26 circles."""
16
+ if num_circles != 26:
17
+ raise ValueError("This CirclePacker is specialized for exactly 26 circles.")
18
+ self.n = num_circles
19
+ self.centers = np.zeros((self.n, 2))
20
+ self.radii = np.zeros(self.n)
21
+
22
+ @staticmethod
23
+ def _compute_max_radii_static(centers: np.ndarray, n: int) -> np.ndarray:
24
+ """
25
+ Computes maximum radii using an iterative method with an adaptive growth
26
+ factor and tolerance with exponential decay, adopted from the highest-scoring
27
+ prior implementations for superior performance.
28
+
29
+ Args:
30
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
31
+ n (int): Number of circles (passed explicitly for consistency with some priors).
32
+
33
+ Returns:
34
+ np.array of shape (n) with the final radius of each circle.
35
+ """
36
+ radii = np.zeros(n)
37
+
38
+ # Parameters from high-scoring prior programs (score 2.5407)
39
+ growth_factor_start = 1.005
40
+ growth_factor_end = 1.002
41
+ outer_iterations = 400
42
+ tolerance_start = 1e-7
43
+ tolerance_end = 1e-11 # Tighter tolerance for precision
44
+ inner_iterations = 20 # Increased for more robust constraint satisfaction
45
+
46
+ # Initialize radii based on boundary distance
47
+ for i in range(n):
48
+ x, y = centers[i]
49
+ radii[i] = min(x, 1 - x, y, 1 - y)
50
+
51
+ # Iteratively grow and resolve constraints
52
+ for k in range(outer_iterations):
53
+ progress = k / (outer_iterations - 1 + 1e-9)
54
+ # Exponential interpolation for smooth parameter transition
55
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**progress
56
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**progress
57
+
58
+ radii *= current_growth_factor
59
+
60
+ for _ in range(inner_iterations):
61
+ constraints_changed = False
62
+ # Boundary constraints
63
+ for i in range(n):
64
+ x, y = centers[i]
65
+ boundary_limit = min(x, 1 - x, y, 1 - y)
66
+ if radii[i] > boundary_limit + current_tolerance:
67
+ radii[i] = boundary_limit
68
+ constraints_changed = True
69
+
70
+ # Overlap constraints
71
+ for i in range(n):
72
+ for j in range(i + 1, n):
73
+ dist = np.linalg.norm(centers[i] - centers[j])
74
+ if radii[i] + radii[j] > dist + current_tolerance:
75
+ total_radius = radii[i] + radii[j]
76
+ if total_radius > 1e-12: # Avoid division by zero with a robust threshold
77
+ scale = dist / total_radius
78
+ radii[i] *= scale
79
+ radii[j] *= scale
80
+ constraints_changed = True
81
+ if not constraints_changed:
82
+ break
83
+ return radii
84
+
85
+ def _initial_grid_placement(self) -> np.ndarray:
86
+ """Places the first 25 circles in a perfect 5x5 grid."""
87
+ coords = np.linspace(0.1, 0.9, 5)
88
+ return np.array(list(product(coords, coords)))
89
+
90
+ def _grid_search_for_26th_circle(self, base_centers: np.ndarray) -> tuple[np.ndarray, float]:
91
+ """
92
+ Performs a refined grid search for the 26th circle by perturbing around core interstitial points.
93
+ """
94
+ interstitial_core_coords = np.linspace(0.2, 0.8, 4)
95
+ delta = 0.025
96
+ perturbation_offsets = np.array([-delta, 0, delta])
97
+
98
+ candidate_points = []
99
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
100
+ for offset_x, offset_y in product(perturbation_offsets, perturbation_offsets):
101
+ candidate_points.append([base_x + offset_x, base_y + offset_y])
102
+
103
+ best_sum_radii = -1.0
104
+ best_centers_config = None
105
+
106
+ for candidate_pos in candidate_points:
107
+ trial_centers = np.vstack([base_centers, np.clip(candidate_pos, 0.0, 1.0)])
108
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
109
+ current_sum_radii = np.sum(trial_radii)
110
+
111
+ if current_sum_radii > best_sum_radii:
112
+ best_sum_radii = current_sum_radii
113
+ best_centers_config = trial_centers
114
+
115
+ # Fallback if no valid candidate found (shouldn't happen with proper candidate generation)
116
+ if best_centers_config is None:
117
+ best_centers_config = np.vstack([base_centers, [[0.5, 0.5]]])
118
+ best_sum_radii = np.sum(CirclePacker._compute_max_radii_static(best_centers_config, self.n))
119
+
120
+
121
+ return best_centers_config, best_sum_radii
122
+
123
+ def _local_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
124
+ """
125
+ Applies a localized SA search to fine-tune the positions of the 26th circle and its 4 nearest neighbors.
126
+ """
127
+ # Tuned SA parameters for local cluster refinement (from current high-scoring program)
128
+ sa_iterations = 300
129
+ sa_initial_temp = 0.0002
130
+ sa_cooling_rate = 0.99
131
+ sa_initial_step_size = 0.005
132
+
133
+ current_centers = np.copy(initial_centers)
134
+ current_sum_radii = initial_sum_radii
135
+
136
+ best_centers = np.copy(current_centers)
137
+ best_sum_radii = current_sum_radii
138
+
139
+ temp = sa_initial_temp
140
+ step_size = sa_initial_step_size
141
+
142
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
143
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
144
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
145
+ cluster_indices = np.append(closest_neighbor_indices, 25)
146
+
147
+ for _ in range(sa_iterations):
148
+ idx_to_move = np.random.choice(cluster_indices)
149
+
150
+ trial_centers = np.copy(current_centers)
151
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
152
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
153
+
154
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
155
+ trial_sum_radii = np.sum(trial_radii)
156
+
157
+ delta_energy = trial_sum_radii - current_sum_radii
158
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
159
+ current_centers = trial_centers
160
+ current_sum_radii = trial_sum_radii
161
+
162
+ if current_sum_radii > best_sum_radii:
163
+ best_sum_radii = current_sum_radii
164
+ best_centers = np.copy(current_centers)
165
+
166
+ temp *= sa_cooling_rate
167
+ step_size = max(step_size * sa_cooling_rate, 1e-7)
168
+
169
+ return best_centers, best_sum_radii
170
+
171
+ def _global_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
172
+ """
173
+ Applies a global SA to "jiggle" all circles into a better global optimum,
174
+ using parameters from the crossover inspiration for broader exploration.
175
+ """
176
+ # SA parameters for a final, gentle, global refinement (from crossover inspiration)
177
+ sa_iterations = 2000 # Increased iterations
178
+ sa_initial_temp = 0.0005 # Higher initial temperature
179
+ sa_cooling_rate = 0.997 # Slower cooling rate
180
+ sa_initial_step_size = 0.01 # Larger initial step size
181
+
182
+ current_centers = np.copy(initial_centers)
183
+ current_sum_radii = initial_sum_radii
184
+
185
+ best_centers = np.copy(current_centers)
186
+ best_sum_radii = current_sum_radii
187
+
188
+ temp = sa_initial_temp
189
+ step_size = sa_initial_step_size
190
+
191
+ for k in range(sa_iterations):
192
+ # Dynamic temperature and step size schedules
193
+ temp = sa_initial_temp * (sa_cooling_rate ** k)
194
+ # Linearly decreasing step size for a more controlled search towards the end
195
+ step_size = sa_initial_step_size * (1.0 - k / sa_iterations)
196
+
197
+ # Perturb a random circle from the entire set
198
+ idx_to_move = np.random.randint(self.n)
199
+
200
+ trial_centers = np.copy(current_centers)
201
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
202
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
203
+
204
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
205
+ trial_sum_radii = np.sum(trial_radii)
206
+
207
+ delta_energy = trial_sum_radii - current_sum_radii
208
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
209
+ current_centers = trial_centers
210
+ current_sum_radii = trial_sum_radii
211
+
212
+ if current_sum_radii > best_sum_radii:
213
+ best_sum_radii = current_sum_radii
214
+ best_centers = np.copy(current_centers)
215
+
216
+ # Ensure step_size doesn't go below a useful minimum
217
+ step_size = max(step_size, 5e-8)
218
+
219
+ return best_centers, best_sum_radii
220
+
221
+ def construct_packing(self):
222
+ """
223
+ Orchestrates the multi-stage packing process:
224
+ 1. Grid search for 26th circle.
225
+ 2. Local SA refinement of the resulting cluster.
226
+ 3. Global SA refinement of the entire packing.
227
+ """
228
+ np.random.seed(42) # For reproducible SA results
229
+
230
+ base_centers = self._initial_grid_placement()
231
+
232
+ # Stage 1: Exhaustive search for a strong starting point for the 26th circle
233
+ centers1, sum_radii1 = self._grid_search_for_26th_circle(base_centers)
234
+
235
+ # Stage 2: Local refinement on the cluster (26th circle + neighbors) to fine-tune
236
+ centers2, sum_radii2 = self._local_refinement_sa(centers1, sum_radii1)
237
+
238
+ # Stage 3: Global "gentle jiggle" refinement on all circles with enhanced parameters
239
+ centers3, _ = self._global_refinement_sa(centers2, sum_radii2)
240
+
241
+ self.centers = centers3
242
+ # Final radius calculation for maximum precision on the best-found centers
243
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
244
+
245
+ return self.centers, self.radii
246
+
247
+
248
+ def construct_packing():
249
+ """
250
+ Constructs an arrangement of 26 circles by leveraging a multi-stage
251
+ optimization strategy managed by the CirclePacker class. This involves
252
+ a refined grid search followed by localized and then global Simulated Annealing.
253
+ """
254
+ packer = CirclePacker(num_circles=26)
255
+ centers, radii = packer.construct_packing()
256
+ return centers, radii
257
+ # EVOLVE-BLOCK-END
258
+
259
+
260
+ # This part remains fixed (not evolved)
261
+ def run_packing():
262
+ """Run the circle packing constructor for n=26"""
263
+ centers, radii = construct_packing()
264
+ # Calculate the sum of radii
265
+ sum_radii = np.sum(radii)
266
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_104/original.py ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings using a multi-stage optimization process:
9
+ 1. Initial 5x5 grid placement.
10
+ 2. A refined interstitial grid search for the 26th circle.
11
+ 3. A localized Simulated Annealing (SA) refinement of the resulting cluster.
12
+ """
13
+ def __init__(self, num_circles=26):
14
+ """Initializes the packer for 26 circles."""
15
+ if num_circles != 26:
16
+ raise ValueError("This CirclePacker is specialized for exactly 26 circles.")
17
+ self.n = num_circles
18
+ self.centers = np.zeros((self.n, 2))
19
+ self.radii = np.zeros(self.n)
20
+
21
+ @staticmethod
22
+ def _compute_max_radii_static(centers: np.ndarray) -> np.ndarray:
23
+ """
24
+ Computes maximum radii using an iterative method with an adaptive growth
25
+ factor and tolerance with exponential decay, adopted from the highest-scoring
26
+ prior implementations for superior performance.
27
+
28
+ Args:
29
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
30
+
31
+ Returns:
32
+ np.array of shape (n) with the final radius of each circle.
33
+ """
34
+ n = centers.shape[0]
35
+ radii = np.zeros(n)
36
+
37
+ # Parameters from the high-scoring prior program (score 2.5406)
38
+ growth_factor_start = 1.005
39
+ growth_factor_end = 1.002
40
+ outer_iterations = 400
41
+ tolerance_start = 1e-7
42
+ tolerance_end = 1e-11
43
+ # Increased inner iterations for more robust constraint satisfaction
44
+ inner_iterations = 20
45
+
46
+ # Initialize radii based on boundary distance
47
+ for i in range(n):
48
+ x, y = centers[i]
49
+ radii[i] = min(x, 1 - x, y, 1 - y)
50
+
51
+ # Iteratively grow and resolve constraints
52
+ for k in range(outer_iterations):
53
+ progress = k / (outer_iterations - 1 + 1e-9)
54
+ # Exponential interpolation for smooth parameter transition
55
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**progress
56
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**progress
57
+
58
+ radii *= current_growth_factor
59
+
60
+ for _ in range(inner_iterations):
61
+ constraints_changed = False
62
+ # Boundary constraints
63
+ for i in range(n):
64
+ x, y = centers[i]
65
+ boundary_limit = min(x, 1 - x, y, 1 - y)
66
+ if radii[i] > boundary_limit + current_tolerance:
67
+ radii[i] = boundary_limit
68
+ constraints_changed = True
69
+
70
+ # Overlap constraints
71
+ for i in range(n):
72
+ for j in range(i + 1, n):
73
+ dist = np.linalg.norm(centers[i] - centers[j])
74
+ if radii[i] + radii[j] > dist + current_tolerance:
75
+ total_radius = radii[i] + radii[j]
76
+ if total_radius > 1e-12: # Avoid division by zero
77
+ scale = dist / total_radius
78
+ radii[i] *= scale
79
+ radii[j] *= scale
80
+ constraints_changed = True
81
+ if not constraints_changed:
82
+ break
83
+ return radii
84
+
85
+ def _initial_grid_placement(self) -> np.ndarray:
86
+ """Places the first 25 circles in a perfect 5x5 grid."""
87
+ coords = np.linspace(0.1, 0.9, 5)
88
+ return np.array(list(product(coords, coords)))
89
+
90
+ def _grid_search_for_26th_circle(self, base_centers: np.ndarray) -> tuple[np.ndarray, float]:
91
+ """
92
+ Performs a refined grid search for the 26th circle by perturbing around core interstitial points.
93
+ """
94
+ interstitial_core_coords = np.linspace(0.2, 0.8, 4)
95
+ delta = 0.025
96
+ perturbation_offsets = np.array([-delta, 0, delta])
97
+
98
+ candidate_points = []
99
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
100
+ for offset_x, offset_y in product(perturbation_offsets, perturbation_offsets):
101
+ candidate_points.append([base_x + offset_x, base_y + offset_y])
102
+
103
+ best_sum_radii = -1.0
104
+ best_centers_config = None
105
+
106
+ for candidate_pos in candidate_points:
107
+ trial_centers = np.vstack([base_centers, np.clip(candidate_pos, 0.0, 1.0)])
108
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
109
+ current_sum_radii = np.sum(trial_radii)
110
+
111
+ if current_sum_radii > best_sum_radii:
112
+ best_sum_radii = current_sum_radii
113
+ best_centers_config = trial_centers
114
+
115
+ return best_centers_config, best_sum_radii
116
+
117
+ def _local_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
118
+ """
119
+ Applies a localized SA search to fine-tune the positions of the 26th circle and its 4 nearest neighbors.
120
+ """
121
+ # Tuned SA parameters for local cluster refinement
122
+ sa_iterations = 300
123
+ sa_initial_temp = 0.0002
124
+ sa_cooling_rate = 0.99
125
+ sa_initial_step_size = 0.005
126
+
127
+ current_centers = np.copy(initial_centers)
128
+ current_sum_radii = initial_sum_radii
129
+
130
+ best_centers = np.copy(current_centers)
131
+ best_sum_radii = current_sum_radii
132
+
133
+ temp = sa_initial_temp
134
+ step_size = sa_initial_step_size
135
+
136
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
137
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
138
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
139
+ cluster_indices = np.append(closest_neighbor_indices, 25)
140
+
141
+ for _ in range(sa_iterations):
142
+ idx_to_move = np.random.choice(cluster_indices)
143
+
144
+ trial_centers = np.copy(current_centers)
145
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
146
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
147
+
148
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
149
+ trial_sum_radii = np.sum(trial_radii)
150
+
151
+ delta_energy = trial_sum_radii - current_sum_radii
152
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
153
+ current_centers = trial_centers
154
+ current_sum_radii = trial_sum_radii
155
+
156
+ if current_sum_radii > best_sum_radii:
157
+ best_sum_radii = current_sum_radii
158
+ best_centers = np.copy(current_centers)
159
+
160
+ temp *= sa_cooling_rate
161
+ step_size = max(step_size * sa_cooling_rate, 1e-7)
162
+
163
+ return best_centers, best_sum_radii
164
+
165
+ def _global_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
166
+ """
167
+ Applies a global, low-temperature SA to "jiggle" all circles into a better global optimum.
168
+ """
169
+ # SA parameters for a final, gentle, global refinement
170
+ sa_iterations = 1500
171
+ sa_initial_temp = 1e-5
172
+ sa_cooling_rate = 0.995
173
+ sa_initial_step_size = 0.004
174
+
175
+ current_centers = np.copy(initial_centers)
176
+ current_sum_radii = initial_sum_radii
177
+
178
+ best_centers = np.copy(current_centers)
179
+ best_sum_radii = current_sum_radii
180
+
181
+ temp = sa_initial_temp
182
+ step_size = sa_initial_step_size
183
+
184
+ for _ in range(sa_iterations):
185
+ # Perturb a random circle from the entire set
186
+ idx_to_move = np.random.randint(self.n)
187
+
188
+ trial_centers = np.copy(current_centers)
189
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
190
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
191
+
192
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers)
193
+ trial_sum_radii = np.sum(trial_radii)
194
+
195
+ delta_energy = trial_sum_radii - current_sum_radii
196
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
197
+ current_centers = trial_centers
198
+ current_sum_radii = trial_sum_radii
199
+
200
+ if current_sum_radii > best_sum_radii:
201
+ best_sum_radii = current_sum_radii
202
+ best_centers = np.copy(current_centers)
203
+
204
+ temp *= sa_cooling_rate
205
+ step_size = max(step_size * sa_cooling_rate, 5e-8)
206
+
207
+ return best_centers, best_sum_radii
208
+
209
+ def construct_packing(self):
210
+ """
211
+ Orchestrates the multi-stage packing process:
212
+ 1. Grid search for 26th circle.
213
+ 2. Local SA refinement of the resulting cluster.
214
+ 3. Global SA refinement of the entire packing.
215
+ """
216
+ base_centers = self._initial_grid_placement()
217
+
218
+ # Stage 1: Exhaustive search for a strong starting point
219
+ centers1, sum_radii1 = self._grid_search_for_26th_circle(base_centers)
220
+
221
+ # Stage 2: Local refinement on the cluster to fine-tune
222
+ centers2, sum_radii2 = self._local_refinement_sa(centers1, sum_radii1)
223
+
224
+ # Stage 3: Global "gentle jiggle" refinement on all circles
225
+ centers3, _ = self._global_refinement_sa(centers2, sum_radii2)
226
+
227
+ self.centers = centers3
228
+ # Final radius calculation for maximum precision on the best-found centers
229
+ self.radii = CirclePacker._compute_max_radii_static(self.centers)
230
+
231
+ return self.centers, self.radii
232
+
233
+
234
+ def construct_packing():
235
+ """
236
+ Constructs an arrangement of 26 circles by leveraging a multi-stage
237
+ optimization strategy managed by the CirclePacker class. This involves
238
+ a refined grid search followed by Simulated Annealing for local refinement.
239
+ """
240
+ packer = CirclePacker(num_circles=26)
241
+ centers, radii = packer.construct_packing()
242
+ return centers, radii
243
+ # EVOLVE-BLOCK-END
244
+
245
+
246
+ # This part remains fixed (not evolved)
247
+ def run_packing():
248
+ """Run the circle packing constructor for n=26"""
249
+ centers, radii = construct_packing()
250
+ # Calculate the sum of radii
251
+ sum_radii = np.sum(radii)
252
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_104/rewrite.txt ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings using a multi-stage optimization process:
9
+ 1. Initial 5x5 grid placement.
10
+ 2. A refined interstitial grid search for the 26th circle.
11
+ 3. A localized Simulated Annealing (SA) refinement of the resulting cluster.
12
+ 4. An extended global SA refinement for the entire packing.
13
+ """
14
+ def __init__(self, num_circles=26):
15
+ """Initializes the packer for 26 circles."""
16
+ if num_circles != 26:
17
+ raise ValueError("This CirclePacker is specialized for exactly 26 circles.")
18
+ self.n = num_circles
19
+ self.centers = np.zeros((self.n, 2))
20
+ self.radii = np.zeros(self.n)
21
+
22
+ @staticmethod
23
+ def _compute_max_radii_static(centers: np.ndarray, n: int) -> np.ndarray:
24
+ """
25
+ Computes maximum radii using an iterative method with an adaptive growth
26
+ factor and tolerance with exponential decay, adopted from the highest-scoring
27
+ prior implementations for superior performance.
28
+
29
+ Args:
30
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
31
+ n (int): Number of circles (passed explicitly for consistency with some priors).
32
+
33
+ Returns:
34
+ np.array of shape (n) with the final radius of each circle.
35
+ """
36
+ radii = np.zeros(n)
37
+
38
+ # Parameters from high-scoring prior programs (score 2.5407)
39
+ growth_factor_start = 1.005
40
+ growth_factor_end = 1.002
41
+ outer_iterations = 400
42
+ tolerance_start = 1e-7
43
+ tolerance_end = 1e-11 # Tighter tolerance for precision
44
+ inner_iterations = 20 # Increased for more robust constraint satisfaction
45
+
46
+ # Initialize radii based on boundary distance
47
+ for i in range(n):
48
+ x, y = centers[i]
49
+ radii[i] = min(x, 1 - x, y, 1 - y)
50
+
51
+ # Iteratively grow and resolve constraints
52
+ for k in range(outer_iterations):
53
+ progress = k / (outer_iterations - 1 + 1e-9)
54
+ # Exponential interpolation for smooth parameter transition
55
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**progress
56
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**progress
57
+
58
+ radii *= current_growth_factor
59
+
60
+ for _ in range(inner_iterations):
61
+ constraints_changed = False
62
+ # Boundary constraints
63
+ for i in range(n):
64
+ x, y = centers[i]
65
+ boundary_limit = min(x, 1 - x, y, 1 - y)
66
+ if radii[i] > boundary_limit + current_tolerance:
67
+ radii[i] = boundary_limit
68
+ constraints_changed = True
69
+
70
+ # Overlap constraints
71
+ for i in range(n):
72
+ for j in range(i + 1, n):
73
+ dist = np.linalg.norm(centers[i] - centers[j])
74
+ if radii[i] + radii[j] > dist + current_tolerance:
75
+ total_radius = radii[i] + radii[j]
76
+ if total_radius > 1e-12: # Avoid division by zero with a robust threshold
77
+ scale = dist / total_radius
78
+ radii[i] *= scale
79
+ radii[j] *= scale
80
+ constraints_changed = True
81
+ if not constraints_changed:
82
+ break
83
+ return radii
84
+
85
+ def _initial_grid_placement(self) -> np.ndarray:
86
+ """Places the first 25 circles in a perfect 5x5 grid."""
87
+ coords = np.linspace(0.1, 0.9, 5)
88
+ return np.array(list(product(coords, coords)))
89
+
90
+ def _grid_search_for_26th_circle(self, base_centers: np.ndarray) -> tuple[np.ndarray, float]:
91
+ """
92
+ Performs a refined grid search for the 26th circle by perturbing around core interstitial points.
93
+ """
94
+ interstitial_core_coords = np.linspace(0.2, 0.8, 4)
95
+ delta = 0.025
96
+ perturbation_offsets = np.array([-delta, 0, delta])
97
+
98
+ candidate_points = []
99
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
100
+ for offset_x, offset_y in product(perturbation_offsets, perturbation_offsets):
101
+ candidate_points.append([base_x + offset_x, base_y + offset_y])
102
+
103
+ best_sum_radii = -1.0
104
+ best_centers_config = None
105
+
106
+ for candidate_pos in candidate_points:
107
+ trial_centers = np.vstack([base_centers, np.clip(candidate_pos, 0.0, 1.0)])
108
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
109
+ current_sum_radii = np.sum(trial_radii)
110
+
111
+ if current_sum_radii > best_sum_radii:
112
+ best_sum_radii = current_sum_radii
113
+ best_centers_config = trial_centers
114
+
115
+ # Fallback if no valid candidate found (shouldn't happen with proper candidate generation)
116
+ if best_centers_config is None:
117
+ best_centers_config = np.vstack([base_centers, [[0.5, 0.5]]])
118
+ best_sum_radii = np.sum(CirclePacker._compute_max_radii_static(best_centers_config, self.n))
119
+
120
+
121
+ return best_centers_config, best_sum_radii
122
+
123
+ def _local_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
124
+ """
125
+ Applies a localized SA search to fine-tune the positions of the 26th circle and its 4 nearest neighbors.
126
+ """
127
+ # Tuned SA parameters for local cluster refinement (from current high-scoring program)
128
+ sa_iterations = 300
129
+ sa_initial_temp = 0.0002
130
+ sa_cooling_rate = 0.99
131
+ sa_initial_step_size = 0.005
132
+
133
+ current_centers = np.copy(initial_centers)
134
+ current_sum_radii = initial_sum_radii
135
+
136
+ best_centers = np.copy(current_centers)
137
+ best_sum_radii = current_sum_radii
138
+
139
+ temp = sa_initial_temp
140
+ step_size = sa_initial_step_size
141
+
142
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
143
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
144
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
145
+ cluster_indices = np.append(closest_neighbor_indices, 25)
146
+
147
+ for _ in range(sa_iterations):
148
+ idx_to_move = np.random.choice(cluster_indices)
149
+
150
+ trial_centers = np.copy(current_centers)
151
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
152
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
153
+
154
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
155
+ trial_sum_radii = np.sum(trial_radii)
156
+
157
+ delta_energy = trial_sum_radii - current_sum_radii
158
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
159
+ current_centers = trial_centers
160
+ current_sum_radii = trial_sum_radii
161
+
162
+ if current_sum_radii > best_sum_radii:
163
+ best_sum_radii = current_sum_radii
164
+ best_centers = np.copy(current_centers)
165
+
166
+ temp *= sa_cooling_rate
167
+ step_size = max(step_size * sa_cooling_rate, 1e-7)
168
+
169
+ return best_centers, best_sum_radii
170
+
171
+ def _global_refinement_sa(self, initial_centers: np.ndarray, initial_sum_radii: float) -> tuple[np.ndarray, float]:
172
+ """
173
+ Applies a global SA to "jiggle" all circles into a better global optimum,
174
+ using parameters from the crossover inspiration for broader exploration.
175
+ """
176
+ # SA parameters for a final, gentle, global refinement (from crossover inspiration)
177
+ sa_iterations = 2000 # Increased iterations
178
+ sa_initial_temp = 0.0005 # Higher initial temperature
179
+ sa_cooling_rate = 0.997 # Slower cooling rate
180
+ sa_initial_step_size = 0.01 # Larger initial step size
181
+
182
+ current_centers = np.copy(initial_centers)
183
+ current_sum_radii = initial_sum_radii
184
+
185
+ best_centers = np.copy(current_centers)
186
+ best_sum_radii = current_sum_radii
187
+
188
+ temp = sa_initial_temp
189
+ step_size = sa_initial_step_size
190
+
191
+ for k in range(sa_iterations):
192
+ # Dynamic temperature and step size schedules
193
+ temp = sa_initial_temp * (sa_cooling_rate ** k)
194
+ # Linearly decreasing step size for a more controlled search towards the end
195
+ step_size = sa_initial_step_size * (1.0 - k / sa_iterations)
196
+
197
+ # Perturb a random circle from the entire set
198
+ idx_to_move = np.random.randint(self.n)
199
+
200
+ trial_centers = np.copy(current_centers)
201
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
202
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
203
+
204
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
205
+ trial_sum_radii = np.sum(trial_radii)
206
+
207
+ delta_energy = trial_sum_radii - current_sum_radii
208
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
209
+ current_centers = trial_centers
210
+ current_sum_radii = trial_sum_radii
211
+
212
+ if current_sum_radii > best_sum_radii:
213
+ best_sum_radii = current_sum_radii
214
+ best_centers = np.copy(current_centers)
215
+
216
+ # Ensure step_size doesn't go below a useful minimum
217
+ step_size = max(step_size, 5e-8)
218
+
219
+ return best_centers, best_sum_radii
220
+
221
+ def construct_packing(self):
222
+ """
223
+ Orchestrates the multi-stage packing process:
224
+ 1. Grid search for 26th circle.
225
+ 2. Local SA refinement of the resulting cluster.
226
+ 3. Global SA refinement of the entire packing.
227
+ """
228
+ np.random.seed(42) # For reproducible SA results
229
+
230
+ base_centers = self._initial_grid_placement()
231
+
232
+ # Stage 1: Exhaustive search for a strong starting point for the 26th circle
233
+ centers1, sum_radii1 = self._grid_search_for_26th_circle(base_centers)
234
+
235
+ # Stage 2: Local refinement on the cluster (26th circle + neighbors) to fine-tune
236
+ centers2, sum_radii2 = self._local_refinement_sa(centers1, sum_radii1)
237
+
238
+ # Stage 3: Global "gentle jiggle" refinement on all circles with enhanced parameters
239
+ centers3, _ = self._global_refinement_sa(centers2, sum_radii2)
240
+
241
+ self.centers = centers3
242
+ # Final radius calculation for maximum precision on the best-found centers
243
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
244
+
245
+ return self.centers, self.radii
246
+
247
+
248
+ def construct_packing():
249
+ """
250
+ Constructs an arrangement of 26 circles by leveraging a multi-stage
251
+ optimization strategy managed by the CirclePacker class. This involves
252
+ a refined grid search followed by localized and then global Simulated Annealing.
253
+ """
254
+ packer = CirclePacker(num_circles=26)
255
+ centers, radii = packer.construct_packing()
256
+ return centers, radii
257
+ # EVOLVE-BLOCK-END
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_105/edit.diff ADDED
@@ -0,0 +1,419 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,402 +1,405 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+ from itertools import product
7
+
8
+
9
+ # --- Data Structure ---
10
+ class PackingState:
11
+ """Holds the current state of the circle packing (centers, radii, sum_radii)."""
12
+ def __init__(self, n, centers=None, radii=None):
13
+ self.n = n
14
+ self.centers = np.zeros((n, 2)) if centers is None else centers
15
+ self.radii = np.zeros(n) if radii is None else radii
16
+ self.sum_radii = 0.0 # Will be updated after radii optimization
17
+
18
+ def update_radii(self, new_radii):
19
+ """Updates radii and recalculates the sum of radii."""
20
+ self.radii = new_radii
21
+ self.sum_radii = np.sum(new_radii)
22
+
23
+ def update_centers(self, new_centers):
24
+ """Updates circle centers."""
25
+ self.centers = new_centers
26
+
27
+
28
+ # --- Core Optimization Logic (Radii) ---
29
+ class RadiiOptimizer:
30
+ """
31
+ Computes the maximum possible radii for a given set of circle centers using
32
+ an iterative growth and constraint resolution method.
33
+ Incorporates adaptive growth factor and dynamic tolerance with non-linear (exponential) decay.
34
+ """
35
+ @staticmethod
36
+ def optimize_radii(centers: np.ndarray, n: int) -> np.ndarray:
37
+ """
38
+ Calculates the maximum radii for `n` circles with given `centers`.
39
+
40
+ Args:
41
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
42
+ n (int): Number of circles.
43
+
44
+ Returns:
45
+ np.array: An array of shape (n) containing the final radius of each circle.
46
+ """
47
+ radii = np.zeros(n)
48
+
49
+ # Proven parameters from high-scoring implementations
50
+ growth_factor_start = 1.005
51
+ growth_factor_end = 1.002
52
+ outer_iterations = 400
53
+ tolerance_start = 1e-7
54
+ tolerance_end = 1e-11 # Tighter precision for the final stages
55
+ inner_iterations = 20 # Increased for more robust constraint satisfaction
56
+
57
+ # Initialize radii based on the distance to the square's boundaries.
58
+ for i in range(n):
59
+ x, y = centers[i]
60
+ radii[i] = min(x, 1 - x, y, 1 - y)
61
+
62
+ for k in range(outer_iterations):
63
+ interp_factor = k / (outer_iterations - 1.0 + 1e-9) # Avoid division by zero
64
+
65
+ # Non-linear (exponential) interpolation for smooth parameter transition
66
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**interp_factor
67
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**interp_factor
68
+
69
+ radii *= current_growth_factor # Tentatively grow all radii
70
+
71
+ for _inner_iter_idx in range(inner_iterations):
72
+ constraints_changed = False
73
+
74
+ # Enforce boundary constraints with dynamic tolerance
75
+ for i in range(n):
76
+ x, y = centers[i]
77
+ boundary_limit = min(x, 1 - x, y, 1 - y)
78
+ if radii[i] > boundary_limit + current_tolerance:
79
+ radii[i] = boundary_limit
80
+ constraints_changed = True
81
+
82
+ # Resolve overlaps between circles with dynamic tolerance
83
+ for i in range(n):
84
+ for j in range(i + 1, n):
85
+ dist = np.linalg.norm(centers[i] - centers[j])
86
+ if radii[i] + radii[j] > dist + current_tolerance:
87
+ total_radius = radii[i] + radii[j]
88
+ if total_radius > 1e-12: # Avoid division by zero for extremely small radii
89
+ scale = dist / total_radius
90
+ radii[i] *= scale
91
+ radii[j] *= scale
92
+ constraints_changed = True
93
+
94
+ if not constraints_changed:
95
+ break # Inner loop converged
96
+
97
+ return radii
98
+
99
+
100
+ # --- Placement Strategies (Components of the pipeline) ---
101
+ class GridInitializer:
102
+ """Places the first N_GRID_CIRCLES in a symmetric grid pattern."""
103
+ def __init__(self, n_grid_circles=25):
104
+ self.n_grid_circles = n_grid_circles
105
+
106
+ def apply(self, packing_state: PackingState) -> PackingState:
107
+ """Applies the initial grid placement to the packing state."""
108
+ # Calculate grid size (e.g., 5 for 25 circles)
109
+ grid_dim = int(np.sqrt(self.n_grid_circles))
110
+ coords = np.linspace(0.1, 0.9, grid_dim)
111
+ grid_centers = np.array(list(product(coords, coords)))
112
+ packing_state.centers[:self.n_grid_circles] = grid_centers
113
+ return packing_state
114
+
115
+ class InitialGridRefiner:
116
+ """
117
+ Applies a gentle, local SA refinement to the initial N_GRID_CIRCLES
118
+ to break the initial grid rigidity and find a better base packing.
119
+ """
120
+ - def __init__(self, n_grid_circles=25, num_iterations=500, initial_step_size=0.005,
121
+ - initial_temp=0.005, cooling_rate=0.995):
122
+ + def __init__(self, n_grid_circles=25, num_iterations=750, # Increased iterations
123
+ + initial_step_size=0.005, initial_temp=0.005, cooling_rate=0.995):
124
+ self.n_grid_circles = n_grid_circles
125
+ self.num_iterations = num_iterations
126
+ self.initial_step_size = initial_step_size
127
+ self.initial_temp = initial_temp
128
+ self.cooling_rate = cooling_rate
129
+
130
+ def apply(self, packing_state: PackingState) -> PackingState:
131
+ """Applies SA-based perturbation and refinement to the base circles."""
132
+ if self.n_grid_circles > packing_state.n:
133
+ return packing_state
134
+
135
+ current_centers_subset = np.copy(packing_state.centers[:self.n_grid_circles])
136
+ current_radii_subset = RadiiOptimizer.optimize_radii(current_centers_subset, self.n_grid_circles)
137
+ current_sum_radii_subset = np.sum(current_radii_subset)
138
+
139
+ best_centers_subset_local = np.copy(current_centers_subset)
140
+ best_sum_radii_subset_local = current_sum_radii_subset
141
+
142
+ temp = self.initial_temp
143
+
144
+ for k in range(self.num_iterations):
145
+ progress = k / self.num_iterations
146
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
147
+
148
+ # Perturb ONE random circle from the subset
149
+ idx_to_perturb = np.random.randint(self.n_grid_circles)
150
+ trial_centers_subset = np.copy(current_centers_subset)
151
+
152
+ random_angle = np.random.uniform(0, 2 * np.pi)
153
+ dx, dy = step_size * np.cos(random_angle), step_size * np.sin(random_angle)
154
+ trial_centers_subset[idx_to_perturb] += [dx, dy]
155
+ trial_centers_subset[idx_to_perturb] = np.clip(trial_centers_subset[idx_to_perturb], 0.0, 1.0)
156
+
157
+ # Evaluate the new configuration
158
+ trial_radii_subset = RadiiOptimizer.optimize_radii(trial_centers_subset, self.n_grid_circles)
159
+ trial_sum_radii_subset = np.sum(trial_radii_subset)
160
+
161
+ # Metropolis-Hastings acceptance criterion
162
+ delta_energy = trial_sum_radii_subset - current_sum_radii_subset
163
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
164
+ current_centers_subset = trial_centers_subset
165
+ current_sum_radii_subset = trial_sum_radii_subset
166
+
167
+ if current_sum_radii_subset > best_sum_radii_subset_local:
168
+ best_sum_radii_subset_local = current_sum_radii_subset
169
+ best_centers_subset_local = np.copy(current_centers_subset)
170
+
171
+ temp *= self.cooling_rate
172
+
173
+ packing_state.centers[:self.n_grid_circles] = best_centers_subset_local
174
+ return packing_state
175
+
176
+ class InterstitialSearcher:
177
+ """
178
+ Finds the best initial position for an additional circle (e.g., the 26th)
179
+ by exhaustive search over candidate points.
180
+ """
181
+ - def __init__(self, index_to_place: int, base_circles_count: int = 25, grid_resolution: int = 9):
182
+ + def __init__(self, index_to_place: int, base_circles_count: int = 25, grid_resolution: int = 12): # Increased grid_resolution
183
+ self.index_to_place = index_to_place
184
+ self.base_circles_count = base_circles_count
185
+ - self.grid_resolution = grid_resolution # e.g., 9x9 for 81 candidates
186
+ + self.grid_resolution = grid_resolution # e.g., 12x12 for 144 candidates
187
+
188
+ def apply(self, packing_state: PackingState) -> PackingState:
189
+ """
190
+ Searches for the optimal position for the circle at `index_to_place`.
191
+ """
192
+ if self.index_to_place >= packing_state.n:
193
+ return packing_state
194
+
195
+ base_centers = np.copy(packing_state.centers[:self.base_circles_count])
196
+
197
+ # Increased granularity and broader range for interstitial search (Recommendation 1)
198
+ - interstitial_coords = np.linspace(0.1, 0.9, self.grid_resolution) # e.g., 9 points from 0.1 to 0.9
199
+ + interstitial_coords = np.linspace(0.1, 0.9, self.grid_resolution)
200
+ candidate_points = list(product(interstitial_coords, interstitial_coords))
201
+
202
+ best_sum_radii = -1.0
203
+ optimal_pos = None
204
+
205
+ for candidate_pos in candidate_points:
206
+ trial_centers = np.vstack([base_centers, np.clip(candidate_pos, 0.0, 1.0)])
207
+ # Use RadiiOptimizer for evaluation
208
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, self.base_circles_count + 1)
209
+ current_sum_radii = np.sum(trial_radii)
210
+
211
+ if current_sum_radii > best_sum_radii:
212
+ best_sum_radii = current_sum_radii
213
+ optimal_pos = np.array(candidate_pos)
214
+
215
+ if optimal_pos is not None:
216
+ packing_state.centers[self.index_to_place] = optimal_pos
217
+ else:
218
+ packing_state.centers[self.index_to_place] = [0.5, 0.5] # Fallback
219
+
220
+ return packing_state
221
+
222
+ class LocalSARefiner:
223
+ """
224
+ Applies localized Simulated Annealing to fine-tune positions
225
+ of a target circle and its closest neighbors.
226
+ """
227
+ - def __init__(self, index_to_perturb: int, num_neighbors: int = 3, num_iterations: int = 500,
228
+ + def __init__(self, index_to_perturb: int, num_neighbors: int = 4, # Increased num_neighbors
229
+ + num_iterations: int = 750, # Increased iterations
230
+ initial_step_size: float = 0.01, initial_temp: float = 0.01, cooling_rate: float = 0.98):
231
+ self.index_to_perturb = index_to_perturb
232
+ self.num_neighbors = num_neighbors
233
+ self.num_iterations = num_iterations
234
+ self.initial_step_size = initial_step_size
235
+ self.initial_temp = initial_temp
236
+ self.cooling_rate = cooling_rate
237
+
238
+ def apply(self, packing_state: PackingState) -> PackingState:
239
+ """
240
+ Refines the position of the target circle and its neighbors using SA.
241
+ """
242
+ current_centers = np.copy(packing_state.centers)
243
+ current_radii = RadiiOptimizer.optimize_radii(current_centers, packing_state.n)
244
+ current_sum_radii = np.sum(current_radii)
245
+
246
+ best_centers_local = np.copy(current_centers)
247
+ best_sum_radii_local = current_sum_radii
248
+
249
+ temp = self.initial_temp
250
+
251
+ # Determine which circles to perturb: target and its closest neighbors
252
+ distances = np.linalg.norm(current_centers - current_centers[self.index_to_perturb], axis=1)
253
+ neighbor_indices = np.argsort(distances)[1:1 + self.num_neighbors] # Exclude self
254
+ indices_to_perturb = np.append([self.index_to_perturb], neighbor_indices)
255
+
256
+ for k in range(self.num_iterations):
257
+ progress = k / self.num_iterations
258
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
259
+
260
+ trial_centers = np.copy(current_centers)
261
+
262
+ for idx in indices_to_perturb:
263
+ # Use a slightly smaller step for neighbors to maintain overall structure integrity
264
+ perturb_step_size = step_size if idx == self.index_to_perturb else step_size / 2.0
265
+ random_angle = np.random.uniform(0, 2 * np.pi)
266
+ dx, dy = perturb_step_size * np.cos(random_angle), perturb_step_size * np.sin(random_angle)
267
+ trial_centers[idx] += [dx, dy]
268
+ trial_centers[idx] = np.clip(trial_centers[idx], 0.0, 1.0) # Enforce boundary constraints
269
+
270
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, packing_state.n)
271
+ trial_sum_radii = np.sum(trial_radii)
272
+
273
+ delta_energy = trial_sum_radii - current_sum_radii
274
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
275
+ current_centers = trial_centers
276
+ current_sum_radii = trial_sum_radii
277
+
278
+ if current_sum_radii > best_sum_radii_local:
279
+ best_sum_radii_local = current_sum_radii
280
+ best_centers_local = np.copy(current_centers)
281
+
282
+ temp *= self.cooling_rate
283
+
284
+ packing_state.update_centers(best_centers_local)
285
+ return packing_state
286
+
287
+ class GlobalSARefiner:
288
+ """
289
+ Applies a global Simulated Annealing (SA) refinement phase to all circles
290
+ to help the entire packing relax into a potentially better overall configuration.
291
+ This serves as a 'gentle jiggle' to escape subtle local optima.
292
+ """
293
+ - def __init__(self, num_iterations: int = 2000, initial_step_size: float = 0.005,
294
+ - initial_temp: float = 0.0005, cooling_rate: float = 0.997):
295
+ + def __init__(self, num_iterations: int = 2500, # Increased iterations
296
+ + initial_step_size: float = 0.005,
297
+ + initial_temp: float = 0.0007, # Slightly increased initial temp
298
+ + cooling_rate: float = 0.997):
299
+ self.num_iterations = num_iterations
300
+ self.initial_step_size = initial_step_size
301
+ self.initial_temp = initial_temp
302
+ self.cooling_rate = cooling_rate
303
+
304
+ def apply(self, packing_state: PackingState) -> PackingState:
305
+ """
306
+ Refines the positions of all circles using SA.
307
+ """
308
+ current_centers = np.copy(packing_state.centers)
309
+ current_radii = RadiiOptimizer.optimize_radii(current_centers, packing_state.n)
310
+ current_sum_radii = np.sum(current_radii)
311
+
312
+ best_centers_global = np.copy(current_centers)
313
+ best_sum_radii_global = current_sum_radii
314
+
315
+ temp = self.initial_temp
316
+
317
+ for k in range(self.num_iterations):
318
+ progress = k / self.num_iterations
319
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
320
+
321
+ trial_centers = np.copy(current_centers)
322
+
323
+ # Perturb one random circle from the entire set of 'n' circles
324
+ idx_to_perturb = np.random.randint(packing_state.n)
325
+
326
+ random_angle = np.random.uniform(0, 2 * np.pi)
327
+ dx, dy = step_size * np.cos(random_angle), step_size * np.sin(random_angle)
328
+ trial_centers[idx_to_perturb] += [dx, dy]
329
+ trial_centers[idx_to_perturb] = np.clip(trial_centers[idx_to_perturb], 0.0, 1.0) # Enforce boundary constraints
330
+
331
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, packing_state.n)
332
+ trial_sum_radii = np.sum(trial_radii)
333
+
334
+ delta_energy = trial_sum_radii - current_sum_radii
335
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
336
+ current_centers = trial_centers
337
+ current_sum_radii = trial_sum_radii
338
+
339
+ if current_sum_radii > best_sum_radii_global:
340
+ best_sum_radii_global = current_sum_radii
341
+ best_centers_global = np.copy(current_centers)
342
+
343
+ temp *= self.cooling_rate
344
+
345
+ packing_state.update_centers(best_centers_global)
346
+ return packing_state
347
+
348
+
349
+ # --- Orchestrator (Main control flow) ---
350
+ class PackingOrchestrator:
351
+ """
352
+ Orchestrates the circle packing process using a pipeline of strategies.
353
+ This provides a clear structural separation of concerns.
354
+ """
355
+ def __init__(self, num_circles: int = 26):
356
+ if num_circles != 26:
357
+ raise ValueError("This orchestrator is specialized for exactly 26 circles.")
358
+
359
+ self.n = num_circles
360
+ self.packing_state = PackingState(num_circles)
361
+ self.pipeline = []
362
+
363
+ # Define the packing pipeline stages
364
+ self.pipeline.append(GridInitializer(n_grid_circles=25))
365
+
366
+ # New stage: pre-refine the initial 25-circle grid
367
+ self.pipeline.append(InitialGridRefiner(n_grid_circles=25))
368
+
369
+ if self.n > 25:
370
+ # Stage for placing the 26th circle with an enhanced search
371
+ - self.pipeline.append(InterstitialSearcher(index_to_place=25, base_circles_count=25, grid_resolution=9))
372
+ + self.pipeline.append(InterstitialSearcher(index_to_place=25, base_circles_count=25))
373
+
374
+ # Local refinement on the newly placed circle and its neighbors
375
+ - self.pipeline.append(LocalSARefiner(index_to_perturb=25, num_iterations=500))
376
+ + self.pipeline.append(LocalSARefiner(index_to_perturb=25))
377
+
378
+ # Final global SA refinement for the entire packing
379
+ - self.pipeline.append(GlobalSARefiner(num_iterations=2000))
380
+ + self.pipeline.append(GlobalSARefiner())
381
+
382
+ def construct_packing(self) -> tuple[np.ndarray, np.ndarray]:
383
+ """
384
+ Executes the predefined packing pipeline, applying each strategy sequentially.
385
+ """
386
+ np.random.seed(42) # For reproducible SA results
387
+ for step in self.pipeline:
388
+ self.packing_state = step.apply(self.packing_state)
389
+
390
+ # Final global radius calculation after all center placements/refinements are complete
391
+ final_radii = RadiiOptimizer.optimize_radii(self.packing_state.centers, self.n)
392
+ self.packing_state.update_radii(final_radii)
393
+
394
+ return self.packing_state.centers, self.packing_state.radii
395
+
396
+
397
+ def construct_packing() -> tuple[np.ndarray, np.ndarray]:
398
+ """
399
+ Constructs an arrangement of 26 circles by leveraging a modular,
400
+ pipeline-based optimization strategy to maximize the sum of radii.
401
+
402
+ Returns:
403
+ Tuple of (centers, radii)
404
+ centers: np.array of shape (26, 2) with (x, y) coordinates
405
+ radii: np.array of shape (26) with final radius of each circle
406
+ """
407
+ orchestrator = PackingOrchestrator(num_circles=26)
408
+ centers, radii = orchestrator.construct_packing()
409
+ return centers, radii
410
+ # EVOLVE-BLOCK-END
411
+
412
+
413
+ # This part remains fixed (not evolved)
414
+ def run_packing():
415
+ """Run the circle packing constructor for n=26"""
416
+ centers, radii = construct_packing()
417
+ # Calculate the sum of radii
418
+ sum_radii = np.sum(radii)
419
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_105/main.py ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ # --- Data Structure ---
7
+ class PackingState:
8
+ """Holds the current state of the circle packing (centers, radii, sum_radii)."""
9
+ def __init__(self, n, centers=None, radii=None):
10
+ self.n = n
11
+ self.centers = np.zeros((n, 2)) if centers is None else centers
12
+ self.radii = np.zeros(n) if radii is None else radii
13
+ self.sum_radii = 0.0 # Will be updated after radii optimization
14
+
15
+ def update_radii(self, new_radii):
16
+ """Updates radii and recalculates the sum of radii."""
17
+ self.radii = new_radii
18
+ self.sum_radii = np.sum(new_radii)
19
+
20
+ def update_centers(self, new_centers):
21
+ """Updates circle centers."""
22
+ self.centers = new_centers
23
+
24
+
25
+ # --- Core Optimization Logic (Radii) ---
26
+ class RadiiOptimizer:
27
+ """
28
+ Computes the maximum possible radii for a given set of circle centers using
29
+ an iterative growth and constraint resolution method.
30
+ Incorporates adaptive growth factor and dynamic tolerance with non-linear (exponential) decay.
31
+ """
32
+ @staticmethod
33
+ def optimize_radii(centers: np.ndarray, n: int) -> np.ndarray:
34
+ """
35
+ Calculates the maximum radii for `n` circles with given `centers`.
36
+
37
+ Args:
38
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
39
+ n (int): Number of circles.
40
+
41
+ Returns:
42
+ np.array: An array of shape (n) containing the final radius of each circle.
43
+ """
44
+ radii = np.zeros(n)
45
+
46
+ # Proven parameters from high-scoring implementations
47
+ growth_factor_start = 1.005
48
+ growth_factor_end = 1.002
49
+ outer_iterations = 400
50
+ tolerance_start = 1e-7
51
+ tolerance_end = 1e-11 # Tighter precision for the final stages
52
+ inner_iterations = 20 # Increased for more robust constraint satisfaction
53
+
54
+ # Initialize radii based on the distance to the square's boundaries.
55
+ for i in range(n):
56
+ x, y = centers[i]
57
+ radii[i] = min(x, 1 - x, y, 1 - y)
58
+
59
+ for k in range(outer_iterations):
60
+ interp_factor = k / (outer_iterations - 1.0 + 1e-9) # Avoid division by zero
61
+
62
+ # Non-linear (exponential) interpolation for smooth parameter transition
63
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**interp_factor
64
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**interp_factor
65
+
66
+ radii *= current_growth_factor # Tentatively grow all radii
67
+
68
+ for _inner_iter_idx in range(inner_iterations):
69
+ constraints_changed = False
70
+
71
+ # Enforce boundary constraints with dynamic tolerance
72
+ for i in range(n):
73
+ x, y = centers[i]
74
+ boundary_limit = min(x, 1 - x, y, 1 - y)
75
+ if radii[i] > boundary_limit + current_tolerance:
76
+ radii[i] = boundary_limit
77
+ constraints_changed = True
78
+
79
+ # Resolve overlaps between circles with dynamic tolerance
80
+ for i in range(n):
81
+ for j in range(i + 1, n):
82
+ dist = np.linalg.norm(centers[i] - centers[j])
83
+ if radii[i] + radii[j] > dist + current_tolerance:
84
+ total_radius = radii[i] + radii[j]
85
+ if total_radius > 1e-12: # Avoid division by zero for extremely small radii
86
+ scale = dist / total_radius
87
+ radii[i] *= scale
88
+ radii[j] *= scale
89
+ constraints_changed = True
90
+
91
+ if not constraints_changed:
92
+ break # Inner loop converged
93
+
94
+ return radii
95
+
96
+
97
+ # --- Placement Strategies (Components of the pipeline) ---
98
+ class GridInitializer:
99
+ """Places the first N_GRID_CIRCLES in a symmetric grid pattern."""
100
+ def __init__(self, n_grid_circles=25):
101
+ self.n_grid_circles = n_grid_circles
102
+
103
+ def apply(self, packing_state: PackingState) -> PackingState:
104
+ """Applies the initial grid placement to the packing state."""
105
+ # Calculate grid size (e.g., 5 for 25 circles)
106
+ grid_dim = int(np.sqrt(self.n_grid_circles))
107
+ coords = np.linspace(0.1, 0.9, grid_dim)
108
+ grid_centers = np.array(list(product(coords, coords)))
109
+ packing_state.centers[:self.n_grid_circles] = grid_centers
110
+ return packing_state
111
+
112
+ class InitialGridRefiner:
113
+ """
114
+ Applies a gentle, local SA refinement to the initial N_GRID_CIRCLES
115
+ to break the initial grid rigidity and find a better base packing.
116
+ """
117
+ def __init__(self, n_grid_circles=25, num_iterations=750, # Increased iterations
118
+ initial_step_size=0.005, initial_temp=0.005, cooling_rate=0.995):
119
+ self.n_grid_circles = n_grid_circles
120
+ self.num_iterations = num_iterations
121
+ self.initial_step_size = initial_step_size
122
+ self.initial_temp = initial_temp
123
+ self.cooling_rate = cooling_rate
124
+
125
+ def apply(self, packing_state: PackingState) -> PackingState:
126
+ """Applies SA-based perturbation and refinement to the base circles."""
127
+ if self.n_grid_circles > packing_state.n:
128
+ return packing_state
129
+
130
+ current_centers_subset = np.copy(packing_state.centers[:self.n_grid_circles])
131
+ current_radii_subset = RadiiOptimizer.optimize_radii(current_centers_subset, self.n_grid_circles)
132
+ current_sum_radii_subset = np.sum(current_radii_subset)
133
+
134
+ best_centers_subset_local = np.copy(current_centers_subset)
135
+ best_sum_radii_subset_local = current_sum_radii_subset
136
+
137
+ temp = self.initial_temp
138
+
139
+ for k in range(self.num_iterations):
140
+ progress = k / self.num_iterations
141
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
142
+
143
+ # Perturb ONE random circle from the subset
144
+ idx_to_perturb = np.random.randint(self.n_grid_circles)
145
+ trial_centers_subset = np.copy(current_centers_subset)
146
+
147
+ random_angle = np.random.uniform(0, 2 * np.pi)
148
+ dx, dy = step_size * np.cos(random_angle), step_size * np.sin(random_angle)
149
+ trial_centers_subset[idx_to_perturb] += [dx, dy]
150
+ trial_centers_subset[idx_to_perturb] = np.clip(trial_centers_subset[idx_to_perturb], 0.0, 1.0)
151
+
152
+ # Evaluate the new configuration
153
+ trial_radii_subset = RadiiOptimizer.optimize_radii(trial_centers_subset, self.n_grid_circles)
154
+ trial_sum_radii_subset = np.sum(trial_radii_subset)
155
+
156
+ # Metropolis-Hastings acceptance criterion
157
+ delta_energy = trial_sum_radii_subset - current_sum_radii_subset
158
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
159
+ current_centers_subset = trial_centers_subset
160
+ current_sum_radii_subset = trial_sum_radii_subset
161
+
162
+ if current_sum_radii_subset > best_sum_radii_subset_local:
163
+ best_sum_radii_subset_local = current_sum_radii_subset
164
+ best_centers_subset_local = np.copy(current_centers_subset)
165
+
166
+ temp *= self.cooling_rate
167
+
168
+ packing_state.centers[:self.n_grid_circles] = best_centers_subset_local
169
+ return packing_state
170
+
171
+ class InterstitialSearcher:
172
+ """
173
+ Finds the best initial position for an additional circle (e.g., the 26th)
174
+ by exhaustive search over candidate points.
175
+ """
176
+ def __init__(self, index_to_place: int, base_circles_count: int = 25, grid_resolution: int = 12): # Increased grid_resolution
177
+ self.index_to_place = index_to_place
178
+ self.base_circles_count = base_circles_count
179
+ self.grid_resolution = grid_resolution # e.g., 12x12 for 144 candidates
180
+
181
+ def apply(self, packing_state: PackingState) -> PackingState:
182
+ """
183
+ Searches for the optimal position for the circle at `index_to_place`.
184
+ """
185
+ if self.index_to_place >= packing_state.n:
186
+ return packing_state
187
+
188
+ base_centers = np.copy(packing_state.centers[:self.base_circles_count])
189
+
190
+ # Increased granularity and broader range for interstitial search (Recommendation 1)
191
+ interstitial_coords = np.linspace(0.1, 0.9, self.grid_resolution)
192
+ candidate_points = list(product(interstitial_coords, interstitial_coords))
193
+
194
+ best_sum_radii = -1.0
195
+ optimal_pos = None
196
+
197
+ for candidate_pos in candidate_points:
198
+ trial_centers = np.vstack([base_centers, np.clip(candidate_pos, 0.0, 1.0)])
199
+ # Use RadiiOptimizer for evaluation
200
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, self.base_circles_count + 1)
201
+ current_sum_radii = np.sum(trial_radii)
202
+
203
+ if current_sum_radii > best_sum_radii:
204
+ best_sum_radii = current_sum_radii
205
+ optimal_pos = np.array(candidate_pos)
206
+
207
+ if optimal_pos is not None:
208
+ packing_state.centers[self.index_to_place] = optimal_pos
209
+ else:
210
+ packing_state.centers[self.index_to_place] = [0.5, 0.5] # Fallback
211
+
212
+ return packing_state
213
+
214
+ class LocalSARefiner:
215
+ """
216
+ Applies localized Simulated Annealing to fine-tune positions
217
+ of a target circle and its closest neighbors.
218
+ """
219
+ def __init__(self, index_to_perturb: int, num_neighbors: int = 4, # Increased num_neighbors
220
+ num_iterations: int = 750, # Increased iterations
221
+ initial_step_size: float = 0.01, initial_temp: float = 0.01, cooling_rate: float = 0.98):
222
+ self.index_to_perturb = index_to_perturb
223
+ self.num_neighbors = num_neighbors
224
+ self.num_iterations = num_iterations
225
+ self.initial_step_size = initial_step_size
226
+ self.initial_temp = initial_temp
227
+ self.cooling_rate = cooling_rate
228
+
229
+ def apply(self, packing_state: PackingState) -> PackingState:
230
+ """
231
+ Refines the position of the target circle and its neighbors using SA.
232
+ """
233
+ current_centers = np.copy(packing_state.centers)
234
+ current_radii = RadiiOptimizer.optimize_radii(current_centers, packing_state.n)
235
+ current_sum_radii = np.sum(current_radii)
236
+
237
+ best_centers_local = np.copy(current_centers)
238
+ best_sum_radii_local = current_sum_radii
239
+
240
+ temp = self.initial_temp
241
+
242
+ # Determine which circles to perturb: target and its closest neighbors
243
+ distances = np.linalg.norm(current_centers - current_centers[self.index_to_perturb], axis=1)
244
+ neighbor_indices = np.argsort(distances)[1:1 + self.num_neighbors] # Exclude self
245
+ indices_to_perturb = np.append([self.index_to_perturb], neighbor_indices)
246
+
247
+ for k in range(self.num_iterations):
248
+ progress = k / self.num_iterations
249
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
250
+
251
+ trial_centers = np.copy(current_centers)
252
+
253
+ for idx in indices_to_perturb:
254
+ # Use a slightly smaller step for neighbors to maintain overall structure integrity
255
+ perturb_step_size = step_size if idx == self.index_to_perturb else step_size / 2.0
256
+ random_angle = np.random.uniform(0, 2 * np.pi)
257
+ dx, dy = perturb_step_size * np.cos(random_angle), perturb_step_size * np.sin(random_angle)
258
+ trial_centers[idx] += [dx, dy]
259
+ trial_centers[idx] = np.clip(trial_centers[idx], 0.0, 1.0) # Enforce boundary constraints
260
+
261
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, packing_state.n)
262
+ trial_sum_radii = np.sum(trial_radii)
263
+
264
+ delta_energy = trial_sum_radii - current_sum_radii
265
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
266
+ current_centers = trial_centers
267
+ current_sum_radii = trial_sum_radii
268
+
269
+ if current_sum_radii > best_sum_radii_local:
270
+ best_sum_radii_local = current_sum_radii
271
+ best_centers_local = np.copy(current_centers)
272
+
273
+ temp *= self.cooling_rate
274
+
275
+ packing_state.update_centers(best_centers_local)
276
+ return packing_state
277
+
278
+ class GlobalSARefiner:
279
+ """
280
+ Applies a global Simulated Annealing (SA) refinement phase to all circles
281
+ to help the entire packing relax into a potentially better overall configuration.
282
+ This serves as a 'gentle jiggle' to escape subtle local optima.
283
+ """
284
+ def __init__(self, num_iterations: int = 2500, # Increased iterations
285
+ initial_step_size: float = 0.005,
286
+ initial_temp: float = 0.0007, # Slightly increased initial temp
287
+ cooling_rate: float = 0.997):
288
+ self.num_iterations = num_iterations
289
+ self.initial_step_size = initial_step_size
290
+ self.initial_temp = initial_temp
291
+ self.cooling_rate = cooling_rate
292
+
293
+ def apply(self, packing_state: PackingState) -> PackingState:
294
+ """
295
+ Refines the positions of all circles using SA.
296
+ """
297
+ current_centers = np.copy(packing_state.centers)
298
+ current_radii = RadiiOptimizer.optimize_radii(current_centers, packing_state.n)
299
+ current_sum_radii = np.sum(current_radii)
300
+
301
+ best_centers_global = np.copy(current_centers)
302
+ best_sum_radii_global = current_sum_radii
303
+
304
+ temp = self.initial_temp
305
+
306
+ for k in range(self.num_iterations):
307
+ progress = k / self.num_iterations
308
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
309
+
310
+ trial_centers = np.copy(current_centers)
311
+
312
+ # Perturb one random circle from the entire set of 'n' circles
313
+ idx_to_perturb = np.random.randint(packing_state.n)
314
+
315
+ random_angle = np.random.uniform(0, 2 * np.pi)
316
+ dx, dy = step_size * np.cos(random_angle), step_size * np.sin(random_angle)
317
+ trial_centers[idx_to_perturb] += [dx, dy]
318
+ trial_centers[idx_to_perturb] = np.clip(trial_centers[idx_to_perturb], 0.0, 1.0) # Enforce boundary constraints
319
+
320
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, packing_state.n)
321
+ trial_sum_radii = np.sum(trial_radii)
322
+
323
+ delta_energy = trial_sum_radii - current_sum_radii
324
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
325
+ current_centers = trial_centers
326
+ current_sum_radii = trial_sum_radii
327
+
328
+ if current_sum_radii > best_sum_radii_global:
329
+ best_sum_radii_global = current_sum_radii
330
+ best_centers_global = np.copy(current_centers)
331
+
332
+ temp *= self.cooling_rate
333
+
334
+ packing_state.update_centers(best_centers_global)
335
+ return packing_state
336
+
337
+
338
+ # --- Orchestrator (Main control flow) ---
339
+ class PackingOrchestrator:
340
+ """
341
+ Orchestrates the circle packing process using a pipeline of strategies.
342
+ This provides a clear structural separation of concerns.
343
+ """
344
+ def __init__(self, num_circles: int = 26):
345
+ if num_circles != 26:
346
+ raise ValueError("This orchestrator is specialized for exactly 26 circles.")
347
+
348
+ self.n = num_circles
349
+ self.packing_state = PackingState(num_circles)
350
+ self.pipeline = []
351
+
352
+ # Define the packing pipeline stages
353
+ self.pipeline.append(GridInitializer(n_grid_circles=25))
354
+
355
+ # New stage: pre-refine the initial 25-circle grid
356
+ self.pipeline.append(InitialGridRefiner(n_grid_circles=25))
357
+
358
+ if self.n > 25:
359
+ # Stage for placing the 26th circle with an enhanced search
360
+ self.pipeline.append(InterstitialSearcher(index_to_place=25, base_circles_count=25))
361
+
362
+ # Local refinement on the newly placed circle and its neighbors
363
+ self.pipeline.append(LocalSARefiner(index_to_perturb=25))
364
+
365
+ # Final global SA refinement for the entire packing
366
+ self.pipeline.append(GlobalSARefiner())
367
+
368
+ def construct_packing(self) -> tuple[np.ndarray, np.ndarray]:
369
+ """
370
+ Executes the predefined packing pipeline, applying each strategy sequentially.
371
+ """
372
+ np.random.seed(42) # For reproducible SA results
373
+ for step in self.pipeline:
374
+ self.packing_state = step.apply(self.packing_state)
375
+
376
+ # Final global radius calculation after all center placements/refinements are complete
377
+ final_radii = RadiiOptimizer.optimize_radii(self.packing_state.centers, self.n)
378
+ self.packing_state.update_radii(final_radii)
379
+
380
+ return self.packing_state.centers, self.packing_state.radii
381
+
382
+
383
+ def construct_packing() -> tuple[np.ndarray, np.ndarray]:
384
+ """
385
+ Constructs an arrangement of 26 circles by leveraging a modular,
386
+ pipeline-based optimization strategy to maximize the sum of radii.
387
+
388
+ Returns:
389
+ Tuple of (centers, radii)
390
+ centers: np.array of shape (26, 2) with (x, y) coordinates
391
+ radii: np.array of shape (26) with final radius of each circle
392
+ """
393
+ orchestrator = PackingOrchestrator(num_circles=26)
394
+ centers, radii = orchestrator.construct_packing()
395
+ return centers, radii
396
+ # EVOLVE-BLOCK-END
397
+
398
+
399
+ # This part remains fixed (not evolved)
400
+ def run_packing():
401
+ """Run the circle packing constructor for n=26"""
402
+ centers, radii = construct_packing()
403
+ # Calculate the sum of radii
404
+ sum_radii = np.sum(radii)
405
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_105/original.py ADDED
@@ -0,0 +1,402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ # --- Data Structure ---
7
+ class PackingState:
8
+ """Holds the current state of the circle packing (centers, radii, sum_radii)."""
9
+ def __init__(self, n, centers=None, radii=None):
10
+ self.n = n
11
+ self.centers = np.zeros((n, 2)) if centers is None else centers
12
+ self.radii = np.zeros(n) if radii is None else radii
13
+ self.sum_radii = 0.0 # Will be updated after radii optimization
14
+
15
+ def update_radii(self, new_radii):
16
+ """Updates radii and recalculates the sum of radii."""
17
+ self.radii = new_radii
18
+ self.sum_radii = np.sum(new_radii)
19
+
20
+ def update_centers(self, new_centers):
21
+ """Updates circle centers."""
22
+ self.centers = new_centers
23
+
24
+
25
+ # --- Core Optimization Logic (Radii) ---
26
+ class RadiiOptimizer:
27
+ """
28
+ Computes the maximum possible radii for a given set of circle centers using
29
+ an iterative growth and constraint resolution method.
30
+ Incorporates adaptive growth factor and dynamic tolerance with non-linear (exponential) decay.
31
+ """
32
+ @staticmethod
33
+ def optimize_radii(centers: np.ndarray, n: int) -> np.ndarray:
34
+ """
35
+ Calculates the maximum radii for `n` circles with given `centers`.
36
+
37
+ Args:
38
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
39
+ n (int): Number of circles.
40
+
41
+ Returns:
42
+ np.array: An array of shape (n) containing the final radius of each circle.
43
+ """
44
+ radii = np.zeros(n)
45
+
46
+ # Proven parameters from high-scoring implementations
47
+ growth_factor_start = 1.005
48
+ growth_factor_end = 1.002
49
+ outer_iterations = 400
50
+ tolerance_start = 1e-7
51
+ tolerance_end = 1e-11 # Tighter precision for the final stages
52
+ inner_iterations = 20 # Increased for more robust constraint satisfaction
53
+
54
+ # Initialize radii based on the distance to the square's boundaries.
55
+ for i in range(n):
56
+ x, y = centers[i]
57
+ radii[i] = min(x, 1 - x, y, 1 - y)
58
+
59
+ for k in range(outer_iterations):
60
+ interp_factor = k / (outer_iterations - 1.0 + 1e-9) # Avoid division by zero
61
+
62
+ # Non-linear (exponential) interpolation for smooth parameter transition
63
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**interp_factor
64
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**interp_factor
65
+
66
+ radii *= current_growth_factor # Tentatively grow all radii
67
+
68
+ for _inner_iter_idx in range(inner_iterations):
69
+ constraints_changed = False
70
+
71
+ # Enforce boundary constraints with dynamic tolerance
72
+ for i in range(n):
73
+ x, y = centers[i]
74
+ boundary_limit = min(x, 1 - x, y, 1 - y)
75
+ if radii[i] > boundary_limit + current_tolerance:
76
+ radii[i] = boundary_limit
77
+ constraints_changed = True
78
+
79
+ # Resolve overlaps between circles with dynamic tolerance
80
+ for i in range(n):
81
+ for j in range(i + 1, n):
82
+ dist = np.linalg.norm(centers[i] - centers[j])
83
+ if radii[i] + radii[j] > dist + current_tolerance:
84
+ total_radius = radii[i] + radii[j]
85
+ if total_radius > 1e-12: # Avoid division by zero for extremely small radii
86
+ scale = dist / total_radius
87
+ radii[i] *= scale
88
+ radii[j] *= scale
89
+ constraints_changed = True
90
+
91
+ if not constraints_changed:
92
+ break # Inner loop converged
93
+
94
+ return radii
95
+
96
+
97
+ # --- Placement Strategies (Components of the pipeline) ---
98
+ class GridInitializer:
99
+ """Places the first N_GRID_CIRCLES in a symmetric grid pattern."""
100
+ def __init__(self, n_grid_circles=25):
101
+ self.n_grid_circles = n_grid_circles
102
+
103
+ def apply(self, packing_state: PackingState) -> PackingState:
104
+ """Applies the initial grid placement to the packing state."""
105
+ # Calculate grid size (e.g., 5 for 25 circles)
106
+ grid_dim = int(np.sqrt(self.n_grid_circles))
107
+ coords = np.linspace(0.1, 0.9, grid_dim)
108
+ grid_centers = np.array(list(product(coords, coords)))
109
+ packing_state.centers[:self.n_grid_circles] = grid_centers
110
+ return packing_state
111
+
112
+ class InitialGridRefiner:
113
+ """
114
+ Applies a gentle, local SA refinement to the initial N_GRID_CIRCLES
115
+ to break the initial grid rigidity and find a better base packing.
116
+ """
117
+ def __init__(self, n_grid_circles=25, num_iterations=500, initial_step_size=0.005,
118
+ initial_temp=0.005, cooling_rate=0.995):
119
+ self.n_grid_circles = n_grid_circles
120
+ self.num_iterations = num_iterations
121
+ self.initial_step_size = initial_step_size
122
+ self.initial_temp = initial_temp
123
+ self.cooling_rate = cooling_rate
124
+
125
+ def apply(self, packing_state: PackingState) -> PackingState:
126
+ """Applies SA-based perturbation and refinement to the base circles."""
127
+ if self.n_grid_circles > packing_state.n:
128
+ return packing_state
129
+
130
+ current_centers_subset = np.copy(packing_state.centers[:self.n_grid_circles])
131
+ current_radii_subset = RadiiOptimizer.optimize_radii(current_centers_subset, self.n_grid_circles)
132
+ current_sum_radii_subset = np.sum(current_radii_subset)
133
+
134
+ best_centers_subset_local = np.copy(current_centers_subset)
135
+ best_sum_radii_subset_local = current_sum_radii_subset
136
+
137
+ temp = self.initial_temp
138
+
139
+ for k in range(self.num_iterations):
140
+ progress = k / self.num_iterations
141
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
142
+
143
+ # Perturb ONE random circle from the subset
144
+ idx_to_perturb = np.random.randint(self.n_grid_circles)
145
+ trial_centers_subset = np.copy(current_centers_subset)
146
+
147
+ random_angle = np.random.uniform(0, 2 * np.pi)
148
+ dx, dy = step_size * np.cos(random_angle), step_size * np.sin(random_angle)
149
+ trial_centers_subset[idx_to_perturb] += [dx, dy]
150
+ trial_centers_subset[idx_to_perturb] = np.clip(trial_centers_subset[idx_to_perturb], 0.0, 1.0)
151
+
152
+ # Evaluate the new configuration
153
+ trial_radii_subset = RadiiOptimizer.optimize_radii(trial_centers_subset, self.n_grid_circles)
154
+ trial_sum_radii_subset = np.sum(trial_radii_subset)
155
+
156
+ # Metropolis-Hastings acceptance criterion
157
+ delta_energy = trial_sum_radii_subset - current_sum_radii_subset
158
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
159
+ current_centers_subset = trial_centers_subset
160
+ current_sum_radii_subset = trial_sum_radii_subset
161
+
162
+ if current_sum_radii_subset > best_sum_radii_subset_local:
163
+ best_sum_radii_subset_local = current_sum_radii_subset
164
+ best_centers_subset_local = np.copy(current_centers_subset)
165
+
166
+ temp *= self.cooling_rate
167
+
168
+ packing_state.centers[:self.n_grid_circles] = best_centers_subset_local
169
+ return packing_state
170
+
171
+ class InterstitialSearcher:
172
+ """
173
+ Finds the best initial position for an additional circle (e.g., the 26th)
174
+ by exhaustive search over candidate points.
175
+ """
176
+ def __init__(self, index_to_place: int, base_circles_count: int = 25, grid_resolution: int = 9):
177
+ self.index_to_place = index_to_place
178
+ self.base_circles_count = base_circles_count
179
+ self.grid_resolution = grid_resolution # e.g., 9x9 for 81 candidates
180
+
181
+ def apply(self, packing_state: PackingState) -> PackingState:
182
+ """
183
+ Searches for the optimal position for the circle at `index_to_place`.
184
+ """
185
+ if self.index_to_place >= packing_state.n:
186
+ return packing_state
187
+
188
+ base_centers = np.copy(packing_state.centers[:self.base_circles_count])
189
+
190
+ # Increased granularity and broader range for interstitial search (Recommendation 1)
191
+ interstitial_coords = np.linspace(0.1, 0.9, self.grid_resolution) # e.g., 9 points from 0.1 to 0.9
192
+ candidate_points = list(product(interstitial_coords, interstitial_coords))
193
+
194
+ best_sum_radii = -1.0
195
+ optimal_pos = None
196
+
197
+ for candidate_pos in candidate_points:
198
+ trial_centers = np.vstack([base_centers, np.clip(candidate_pos, 0.0, 1.0)])
199
+ # Use RadiiOptimizer for evaluation
200
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, self.base_circles_count + 1)
201
+ current_sum_radii = np.sum(trial_radii)
202
+
203
+ if current_sum_radii > best_sum_radii:
204
+ best_sum_radii = current_sum_radii
205
+ optimal_pos = np.array(candidate_pos)
206
+
207
+ if optimal_pos is not None:
208
+ packing_state.centers[self.index_to_place] = optimal_pos
209
+ else:
210
+ packing_state.centers[self.index_to_place] = [0.5, 0.5] # Fallback
211
+
212
+ return packing_state
213
+
214
+ class LocalSARefiner:
215
+ """
216
+ Applies localized Simulated Annealing to fine-tune positions
217
+ of a target circle and its closest neighbors.
218
+ """
219
+ def __init__(self, index_to_perturb: int, num_neighbors: int = 3, num_iterations: int = 500,
220
+ initial_step_size: float = 0.01, initial_temp: float = 0.01, cooling_rate: float = 0.98):
221
+ self.index_to_perturb = index_to_perturb
222
+ self.num_neighbors = num_neighbors
223
+ self.num_iterations = num_iterations
224
+ self.initial_step_size = initial_step_size
225
+ self.initial_temp = initial_temp
226
+ self.cooling_rate = cooling_rate
227
+
228
+ def apply(self, packing_state: PackingState) -> PackingState:
229
+ """
230
+ Refines the position of the target circle and its neighbors using SA.
231
+ """
232
+ current_centers = np.copy(packing_state.centers)
233
+ current_radii = RadiiOptimizer.optimize_radii(current_centers, packing_state.n)
234
+ current_sum_radii = np.sum(current_radii)
235
+
236
+ best_centers_local = np.copy(current_centers)
237
+ best_sum_radii_local = current_sum_radii
238
+
239
+ temp = self.initial_temp
240
+
241
+ # Determine which circles to perturb: target and its closest neighbors
242
+ distances = np.linalg.norm(current_centers - current_centers[self.index_to_perturb], axis=1)
243
+ neighbor_indices = np.argsort(distances)[1:1 + self.num_neighbors] # Exclude self
244
+ indices_to_perturb = np.append([self.index_to_perturb], neighbor_indices)
245
+
246
+ for k in range(self.num_iterations):
247
+ progress = k / self.num_iterations
248
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
249
+
250
+ trial_centers = np.copy(current_centers)
251
+
252
+ for idx in indices_to_perturb:
253
+ # Use a slightly smaller step for neighbors to maintain overall structure integrity
254
+ perturb_step_size = step_size if idx == self.index_to_perturb else step_size / 2.0
255
+ random_angle = np.random.uniform(0, 2 * np.pi)
256
+ dx, dy = perturb_step_size * np.cos(random_angle), perturb_step_size * np.sin(random_angle)
257
+ trial_centers[idx] += [dx, dy]
258
+ trial_centers[idx] = np.clip(trial_centers[idx], 0.0, 1.0) # Enforce boundary constraints
259
+
260
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, packing_state.n)
261
+ trial_sum_radii = np.sum(trial_radii)
262
+
263
+ delta_energy = trial_sum_radii - current_sum_radii
264
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
265
+ current_centers = trial_centers
266
+ current_sum_radii = trial_sum_radii
267
+
268
+ if current_sum_radii > best_sum_radii_local:
269
+ best_sum_radii_local = current_sum_radii
270
+ best_centers_local = np.copy(current_centers)
271
+
272
+ temp *= self.cooling_rate
273
+
274
+ packing_state.update_centers(best_centers_local)
275
+ return packing_state
276
+
277
+ class GlobalSARefiner:
278
+ """
279
+ Applies a global Simulated Annealing (SA) refinement phase to all circles
280
+ to help the entire packing relax into a potentially better overall configuration.
281
+ This serves as a 'gentle jiggle' to escape subtle local optima.
282
+ """
283
+ def __init__(self, num_iterations: int = 2000, initial_step_size: float = 0.005,
284
+ initial_temp: float = 0.0005, cooling_rate: float = 0.997):
285
+ self.num_iterations = num_iterations
286
+ self.initial_step_size = initial_step_size
287
+ self.initial_temp = initial_temp
288
+ self.cooling_rate = cooling_rate
289
+
290
+ def apply(self, packing_state: PackingState) -> PackingState:
291
+ """
292
+ Refines the positions of all circles using SA.
293
+ """
294
+ current_centers = np.copy(packing_state.centers)
295
+ current_radii = RadiiOptimizer.optimize_radii(current_centers, packing_state.n)
296
+ current_sum_radii = np.sum(current_radii)
297
+
298
+ best_centers_global = np.copy(current_centers)
299
+ best_sum_radii_global = current_sum_radii
300
+
301
+ temp = self.initial_temp
302
+
303
+ for k in range(self.num_iterations):
304
+ progress = k / self.num_iterations
305
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
306
+
307
+ trial_centers = np.copy(current_centers)
308
+
309
+ # Perturb one random circle from the entire set of 'n' circles
310
+ idx_to_perturb = np.random.randint(packing_state.n)
311
+
312
+ random_angle = np.random.uniform(0, 2 * np.pi)
313
+ dx, dy = step_size * np.cos(random_angle), step_size * np.sin(random_angle)
314
+ trial_centers[idx_to_perturb] += [dx, dy]
315
+ trial_centers[idx_to_perturb] = np.clip(trial_centers[idx_to_perturb], 0.0, 1.0) # Enforce boundary constraints
316
+
317
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, packing_state.n)
318
+ trial_sum_radii = np.sum(trial_radii)
319
+
320
+ delta_energy = trial_sum_radii - current_sum_radii
321
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
322
+ current_centers = trial_centers
323
+ current_sum_radii = trial_sum_radii
324
+
325
+ if current_sum_radii > best_sum_radii_global:
326
+ best_sum_radii_global = current_sum_radii
327
+ best_centers_global = np.copy(current_centers)
328
+
329
+ temp *= self.cooling_rate
330
+
331
+ packing_state.update_centers(best_centers_global)
332
+ return packing_state
333
+
334
+
335
+ # --- Orchestrator (Main control flow) ---
336
+ class PackingOrchestrator:
337
+ """
338
+ Orchestrates the circle packing process using a pipeline of strategies.
339
+ This provides a clear structural separation of concerns.
340
+ """
341
+ def __init__(self, num_circles: int = 26):
342
+ if num_circles != 26:
343
+ raise ValueError("This orchestrator is specialized for exactly 26 circles.")
344
+
345
+ self.n = num_circles
346
+ self.packing_state = PackingState(num_circles)
347
+ self.pipeline = []
348
+
349
+ # Define the packing pipeline stages
350
+ self.pipeline.append(GridInitializer(n_grid_circles=25))
351
+
352
+ # New stage: pre-refine the initial 25-circle grid
353
+ self.pipeline.append(InitialGridRefiner(n_grid_circles=25))
354
+
355
+ if self.n > 25:
356
+ # Stage for placing the 26th circle with an enhanced search
357
+ self.pipeline.append(InterstitialSearcher(index_to_place=25, base_circles_count=25, grid_resolution=9))
358
+
359
+ # Local refinement on the newly placed circle and its neighbors
360
+ self.pipeline.append(LocalSARefiner(index_to_perturb=25, num_iterations=500))
361
+
362
+ # Final global SA refinement for the entire packing
363
+ self.pipeline.append(GlobalSARefiner(num_iterations=2000))
364
+
365
+ def construct_packing(self) -> tuple[np.ndarray, np.ndarray]:
366
+ """
367
+ Executes the predefined packing pipeline, applying each strategy sequentially.
368
+ """
369
+ np.random.seed(42) # For reproducible SA results
370
+ for step in self.pipeline:
371
+ self.packing_state = step.apply(self.packing_state)
372
+
373
+ # Final global radius calculation after all center placements/refinements are complete
374
+ final_radii = RadiiOptimizer.optimize_radii(self.packing_state.centers, self.n)
375
+ self.packing_state.update_radii(final_radii)
376
+
377
+ return self.packing_state.centers, self.packing_state.radii
378
+
379
+
380
+ def construct_packing() -> tuple[np.ndarray, np.ndarray]:
381
+ """
382
+ Constructs an arrangement of 26 circles by leveraging a modular,
383
+ pipeline-based optimization strategy to maximize the sum of radii.
384
+
385
+ Returns:
386
+ Tuple of (centers, radii)
387
+ centers: np.array of shape (26, 2) with (x, y) coordinates
388
+ radii: np.array of shape (26) with final radius of each circle
389
+ """
390
+ orchestrator = PackingOrchestrator(num_circles=26)
391
+ centers, radii = orchestrator.construct_packing()
392
+ return centers, radii
393
+ # EVOLVE-BLOCK-END
394
+
395
+
396
+ # This part remains fixed (not evolved)
397
+ def run_packing():
398
+ """Run the circle packing constructor for n=26"""
399
+ centers, radii = construct_packing()
400
+ # Calculate the sum of radii
401
+ sum_radii = np.sum(radii)
402
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_105/rewrite.txt ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ # --- Data Structure ---
7
+ class PackingState:
8
+ """Holds the current state of the circle packing (centers, radii, sum_radii)."""
9
+ def __init__(self, n, centers=None, radii=None):
10
+ self.n = n
11
+ self.centers = np.zeros((n, 2)) if centers is None else centers
12
+ self.radii = np.zeros(n) if radii is None else radii
13
+ self.sum_radii = 0.0 # Will be updated after radii optimization
14
+
15
+ def update_radii(self, new_radii):
16
+ """Updates radii and recalculates the sum of radii."""
17
+ self.radii = new_radii
18
+ self.sum_radii = np.sum(new_radii)
19
+
20
+ def update_centers(self, new_centers):
21
+ """Updates circle centers."""
22
+ self.centers = new_centers
23
+
24
+
25
+ # --- Core Optimization Logic (Radii) ---
26
+ class RadiiOptimizer:
27
+ """
28
+ Computes the maximum possible radii for a given set of circle centers using
29
+ an iterative growth and constraint resolution method.
30
+ Incorporates adaptive growth factor and dynamic tolerance with non-linear (exponential) decay.
31
+ """
32
+ @staticmethod
33
+ def optimize_radii(centers: np.ndarray, n: int) -> np.ndarray:
34
+ """
35
+ Calculates the maximum radii for `n` circles with given `centers`.
36
+
37
+ Args:
38
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
39
+ n (int): Number of circles.
40
+
41
+ Returns:
42
+ np.array: An array of shape (n) containing the final radius of each circle.
43
+ """
44
+ radii = np.zeros(n)
45
+
46
+ # Proven parameters from high-scoring implementations
47
+ growth_factor_start = 1.005
48
+ growth_factor_end = 1.002
49
+ outer_iterations = 400
50
+ tolerance_start = 1e-7
51
+ tolerance_end = 1e-11 # Tighter precision for the final stages
52
+ inner_iterations = 20 # Increased for more robust constraint satisfaction
53
+
54
+ # Initialize radii based on the distance to the square's boundaries.
55
+ for i in range(n):
56
+ x, y = centers[i]
57
+ radii[i] = min(x, 1 - x, y, 1 - y)
58
+
59
+ for k in range(outer_iterations):
60
+ interp_factor = k / (outer_iterations - 1.0 + 1e-9) # Avoid division by zero
61
+
62
+ # Non-linear (exponential) interpolation for smooth parameter transition
63
+ current_growth_factor = growth_factor_start * (growth_factor_end / growth_factor_start)**interp_factor
64
+ current_tolerance = tolerance_start * (tolerance_end / tolerance_start)**interp_factor
65
+
66
+ radii *= current_growth_factor # Tentatively grow all radii
67
+
68
+ for _inner_iter_idx in range(inner_iterations):
69
+ constraints_changed = False
70
+
71
+ # Enforce boundary constraints with dynamic tolerance
72
+ for i in range(n):
73
+ x, y = centers[i]
74
+ boundary_limit = min(x, 1 - x, y, 1 - y)
75
+ if radii[i] > boundary_limit + current_tolerance:
76
+ radii[i] = boundary_limit
77
+ constraints_changed = True
78
+
79
+ # Resolve overlaps between circles with dynamic tolerance
80
+ for i in range(n):
81
+ for j in range(i + 1, n):
82
+ dist = np.linalg.norm(centers[i] - centers[j])
83
+ if radii[i] + radii[j] > dist + current_tolerance:
84
+ total_radius = radii[i] + radii[j]
85
+ if total_radius > 1e-12: # Avoid division by zero for extremely small radii
86
+ scale = dist / total_radius
87
+ radii[i] *= scale
88
+ radii[j] *= scale
89
+ constraints_changed = True
90
+
91
+ if not constraints_changed:
92
+ break # Inner loop converged
93
+
94
+ return radii
95
+
96
+
97
+ # --- Placement Strategies (Components of the pipeline) ---
98
+ class GridInitializer:
99
+ """Places the first N_GRID_CIRCLES in a symmetric grid pattern."""
100
+ def __init__(self, n_grid_circles=25):
101
+ self.n_grid_circles = n_grid_circles
102
+
103
+ def apply(self, packing_state: PackingState) -> PackingState:
104
+ """Applies the initial grid placement to the packing state."""
105
+ # Calculate grid size (e.g., 5 for 25 circles)
106
+ grid_dim = int(np.sqrt(self.n_grid_circles))
107
+ coords = np.linspace(0.1, 0.9, grid_dim)
108
+ grid_centers = np.array(list(product(coords, coords)))
109
+ packing_state.centers[:self.n_grid_circles] = grid_centers
110
+ return packing_state
111
+
112
+ class InitialGridRefiner:
113
+ """
114
+ Applies a gentle, local SA refinement to the initial N_GRID_CIRCLES
115
+ to break the initial grid rigidity and find a better base packing.
116
+ """
117
+ def __init__(self, n_grid_circles=25, num_iterations=750, # Increased iterations
118
+ initial_step_size=0.005, initial_temp=0.005, cooling_rate=0.995):
119
+ self.n_grid_circles = n_grid_circles
120
+ self.num_iterations = num_iterations
121
+ self.initial_step_size = initial_step_size
122
+ self.initial_temp = initial_temp
123
+ self.cooling_rate = cooling_rate
124
+
125
+ def apply(self, packing_state: PackingState) -> PackingState:
126
+ """Applies SA-based perturbation and refinement to the base circles."""
127
+ if self.n_grid_circles > packing_state.n:
128
+ return packing_state
129
+
130
+ current_centers_subset = np.copy(packing_state.centers[:self.n_grid_circles])
131
+ current_radii_subset = RadiiOptimizer.optimize_radii(current_centers_subset, self.n_grid_circles)
132
+ current_sum_radii_subset = np.sum(current_radii_subset)
133
+
134
+ best_centers_subset_local = np.copy(current_centers_subset)
135
+ best_sum_radii_subset_local = current_sum_radii_subset
136
+
137
+ temp = self.initial_temp
138
+
139
+ for k in range(self.num_iterations):
140
+ progress = k / self.num_iterations
141
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
142
+
143
+ # Perturb ONE random circle from the subset
144
+ idx_to_perturb = np.random.randint(self.n_grid_circles)
145
+ trial_centers_subset = np.copy(current_centers_subset)
146
+
147
+ random_angle = np.random.uniform(0, 2 * np.pi)
148
+ dx, dy = step_size * np.cos(random_angle), step_size * np.sin(random_angle)
149
+ trial_centers_subset[idx_to_perturb] += [dx, dy]
150
+ trial_centers_subset[idx_to_perturb] = np.clip(trial_centers_subset[idx_to_perturb], 0.0, 1.0)
151
+
152
+ # Evaluate the new configuration
153
+ trial_radii_subset = RadiiOptimizer.optimize_radii(trial_centers_subset, self.n_grid_circles)
154
+ trial_sum_radii_subset = np.sum(trial_radii_subset)
155
+
156
+ # Metropolis-Hastings acceptance criterion
157
+ delta_energy = trial_sum_radii_subset - current_sum_radii_subset
158
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
159
+ current_centers_subset = trial_centers_subset
160
+ current_sum_radii_subset = trial_sum_radii_subset
161
+
162
+ if current_sum_radii_subset > best_sum_radii_subset_local:
163
+ best_sum_radii_subset_local = current_sum_radii_subset
164
+ best_centers_subset_local = np.copy(current_centers_subset)
165
+
166
+ temp *= self.cooling_rate
167
+
168
+ packing_state.centers[:self.n_grid_circles] = best_centers_subset_local
169
+ return packing_state
170
+
171
+ class InterstitialSearcher:
172
+ """
173
+ Finds the best initial position for an additional circle (e.g., the 26th)
174
+ by exhaustive search over candidate points.
175
+ """
176
+ def __init__(self, index_to_place: int, base_circles_count: int = 25, grid_resolution: int = 12): # Increased grid_resolution
177
+ self.index_to_place = index_to_place
178
+ self.base_circles_count = base_circles_count
179
+ self.grid_resolution = grid_resolution # e.g., 12x12 for 144 candidates
180
+
181
+ def apply(self, packing_state: PackingState) -> PackingState:
182
+ """
183
+ Searches for the optimal position for the circle at `index_to_place`.
184
+ """
185
+ if self.index_to_place >= packing_state.n:
186
+ return packing_state
187
+
188
+ base_centers = np.copy(packing_state.centers[:self.base_circles_count])
189
+
190
+ # Increased granularity and broader range for interstitial search (Recommendation 1)
191
+ interstitial_coords = np.linspace(0.1, 0.9, self.grid_resolution)
192
+ candidate_points = list(product(interstitial_coords, interstitial_coords))
193
+
194
+ best_sum_radii = -1.0
195
+ optimal_pos = None
196
+
197
+ for candidate_pos in candidate_points:
198
+ trial_centers = np.vstack([base_centers, np.clip(candidate_pos, 0.0, 1.0)])
199
+ # Use RadiiOptimizer for evaluation
200
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, self.base_circles_count + 1)
201
+ current_sum_radii = np.sum(trial_radii)
202
+
203
+ if current_sum_radii > best_sum_radii:
204
+ best_sum_radii = current_sum_radii
205
+ optimal_pos = np.array(candidate_pos)
206
+
207
+ if optimal_pos is not None:
208
+ packing_state.centers[self.index_to_place] = optimal_pos
209
+ else:
210
+ packing_state.centers[self.index_to_place] = [0.5, 0.5] # Fallback
211
+
212
+ return packing_state
213
+
214
+ class LocalSARefiner:
215
+ """
216
+ Applies localized Simulated Annealing to fine-tune positions
217
+ of a target circle and its closest neighbors.
218
+ """
219
+ def __init__(self, index_to_perturb: int, num_neighbors: int = 4, # Increased num_neighbors
220
+ num_iterations: int = 750, # Increased iterations
221
+ initial_step_size: float = 0.01, initial_temp: float = 0.01, cooling_rate: float = 0.98):
222
+ self.index_to_perturb = index_to_perturb
223
+ self.num_neighbors = num_neighbors
224
+ self.num_iterations = num_iterations
225
+ self.initial_step_size = initial_step_size
226
+ self.initial_temp = initial_temp
227
+ self.cooling_rate = cooling_rate
228
+
229
+ def apply(self, packing_state: PackingState) -> PackingState:
230
+ """
231
+ Refines the position of the target circle and its neighbors using SA.
232
+ """
233
+ current_centers = np.copy(packing_state.centers)
234
+ current_radii = RadiiOptimizer.optimize_radii(current_centers, packing_state.n)
235
+ current_sum_radii = np.sum(current_radii)
236
+
237
+ best_centers_local = np.copy(current_centers)
238
+ best_sum_radii_local = current_sum_radii
239
+
240
+ temp = self.initial_temp
241
+
242
+ # Determine which circles to perturb: target and its closest neighbors
243
+ distances = np.linalg.norm(current_centers - current_centers[self.index_to_perturb], axis=1)
244
+ neighbor_indices = np.argsort(distances)[1:1 + self.num_neighbors] # Exclude self
245
+ indices_to_perturb = np.append([self.index_to_perturb], neighbor_indices)
246
+
247
+ for k in range(self.num_iterations):
248
+ progress = k / self.num_iterations
249
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
250
+
251
+ trial_centers = np.copy(current_centers)
252
+
253
+ for idx in indices_to_perturb:
254
+ # Use a slightly smaller step for neighbors to maintain overall structure integrity
255
+ perturb_step_size = step_size if idx == self.index_to_perturb else step_size / 2.0
256
+ random_angle = np.random.uniform(0, 2 * np.pi)
257
+ dx, dy = perturb_step_size * np.cos(random_angle), perturb_step_size * np.sin(random_angle)
258
+ trial_centers[idx] += [dx, dy]
259
+ trial_centers[idx] = np.clip(trial_centers[idx], 0.0, 1.0) # Enforce boundary constraints
260
+
261
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, packing_state.n)
262
+ trial_sum_radii = np.sum(trial_radii)
263
+
264
+ delta_energy = trial_sum_radii - current_sum_radii
265
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
266
+ current_centers = trial_centers
267
+ current_sum_radii = trial_sum_radii
268
+
269
+ if current_sum_radii > best_sum_radii_local:
270
+ best_sum_radii_local = current_sum_radii
271
+ best_centers_local = np.copy(current_centers)
272
+
273
+ temp *= self.cooling_rate
274
+
275
+ packing_state.update_centers(best_centers_local)
276
+ return packing_state
277
+
278
+ class GlobalSARefiner:
279
+ """
280
+ Applies a global Simulated Annealing (SA) refinement phase to all circles
281
+ to help the entire packing relax into a potentially better overall configuration.
282
+ This serves as a 'gentle jiggle' to escape subtle local optima.
283
+ """
284
+ def __init__(self, num_iterations: int = 2500, # Increased iterations
285
+ initial_step_size: float = 0.005,
286
+ initial_temp: float = 0.0007, # Slightly increased initial temp
287
+ cooling_rate: float = 0.997):
288
+ self.num_iterations = num_iterations
289
+ self.initial_step_size = initial_step_size
290
+ self.initial_temp = initial_temp
291
+ self.cooling_rate = cooling_rate
292
+
293
+ def apply(self, packing_state: PackingState) -> PackingState:
294
+ """
295
+ Refines the positions of all circles using SA.
296
+ """
297
+ current_centers = np.copy(packing_state.centers)
298
+ current_radii = RadiiOptimizer.optimize_radii(current_centers, packing_state.n)
299
+ current_sum_radii = np.sum(current_radii)
300
+
301
+ best_centers_global = np.copy(current_centers)
302
+ best_sum_radii_global = current_sum_radii
303
+
304
+ temp = self.initial_temp
305
+
306
+ for k in range(self.num_iterations):
307
+ progress = k / self.num_iterations
308
+ step_size = self.initial_step_size * (1.0 - progress) # Linear decay of step size
309
+
310
+ trial_centers = np.copy(current_centers)
311
+
312
+ # Perturb one random circle from the entire set of 'n' circles
313
+ idx_to_perturb = np.random.randint(packing_state.n)
314
+
315
+ random_angle = np.random.uniform(0, 2 * np.pi)
316
+ dx, dy = step_size * np.cos(random_angle), step_size * np.sin(random_angle)
317
+ trial_centers[idx_to_perturb] += [dx, dy]
318
+ trial_centers[idx_to_perturb] = np.clip(trial_centers[idx_to_perturb], 0.0, 1.0) # Enforce boundary constraints
319
+
320
+ trial_radii = RadiiOptimizer.optimize_radii(trial_centers, packing_state.n)
321
+ trial_sum_radii = np.sum(trial_radii)
322
+
323
+ delta_energy = trial_sum_radii - current_sum_radii
324
+ if delta_energy > 0 or (temp > 0 and np.random.random() < np.exp(delta_energy / temp)):
325
+ current_centers = trial_centers
326
+ current_sum_radii = trial_sum_radii
327
+
328
+ if current_sum_radii > best_sum_radii_global:
329
+ best_sum_radii_global = current_sum_radii
330
+ best_centers_global = np.copy(current_centers)
331
+
332
+ temp *= self.cooling_rate
333
+
334
+ packing_state.update_centers(best_centers_global)
335
+ return packing_state
336
+
337
+
338
+ # --- Orchestrator (Main control flow) ---
339
+ class PackingOrchestrator:
340
+ """
341
+ Orchestrates the circle packing process using a pipeline of strategies.
342
+ This provides a clear structural separation of concerns.
343
+ """
344
+ def __init__(self, num_circles: int = 26):
345
+ if num_circles != 26:
346
+ raise ValueError("This orchestrator is specialized for exactly 26 circles.")
347
+
348
+ self.n = num_circles
349
+ self.packing_state = PackingState(num_circles)
350
+ self.pipeline = []
351
+
352
+ # Define the packing pipeline stages
353
+ self.pipeline.append(GridInitializer(n_grid_circles=25))
354
+
355
+ # New stage: pre-refine the initial 25-circle grid
356
+ self.pipeline.append(InitialGridRefiner(n_grid_circles=25))
357
+
358
+ if self.n > 25:
359
+ # Stage for placing the 26th circle with an enhanced search
360
+ self.pipeline.append(InterstitialSearcher(index_to_place=25, base_circles_count=25))
361
+
362
+ # Local refinement on the newly placed circle and its neighbors
363
+ self.pipeline.append(LocalSARefiner(index_to_perturb=25))
364
+
365
+ # Final global SA refinement for the entire packing
366
+ self.pipeline.append(GlobalSARefiner())
367
+
368
+ def construct_packing(self) -> tuple[np.ndarray, np.ndarray]:
369
+ """
370
+ Executes the predefined packing pipeline, applying each strategy sequentially.
371
+ """
372
+ np.random.seed(42) # For reproducible SA results
373
+ for step in self.pipeline:
374
+ self.packing_state = step.apply(self.packing_state)
375
+
376
+ # Final global radius calculation after all center placements/refinements are complete
377
+ final_radii = RadiiOptimizer.optimize_radii(self.packing_state.centers, self.n)
378
+ self.packing_state.update_radii(final_radii)
379
+
380
+ return self.packing_state.centers, self.packing_state.radii
381
+
382
+
383
+ def construct_packing() -> tuple[np.ndarray, np.ndarray]:
384
+ """
385
+ Constructs an arrangement of 26 circles by leveraging a modular,
386
+ pipeline-based optimization strategy to maximize the sum of radii.
387
+
388
+ Returns:
389
+ Tuple of (centers, radii)
390
+ centers: np.array of shape (26, 2) with (x, y) coordinates
391
+ radii: np.array of shape (26) with final radius of each circle
392
+ """
393
+ orchestrator = PackingOrchestrator(num_circles=26)
394
+ centers, radii = orchestrator.construct_packing()
395
+ return centers, radii
396
+ # EVOLVE-BLOCK-END
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_106/edit.diff ADDED
@@ -0,0 +1,400 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,356 +1,376 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+ from itertools import product
7
+
8
+
9
+ class CirclePacker:
10
+ """
11
+ A class to construct circle packings within a unit square using a hybrid
12
+ optimization approach. It combines an exhaustive search for initial placement
13
+ with a localized refinement stage.
14
+ """
15
+ def __init__(self, num_circles=26):
16
+ self.n = num_circles
17
+ self.centers = np.zeros((self.n, 2))
18
+ self.radii = np.zeros(self.n)
19
+
20
+ @staticmethod
21
+ def _compute_max_radii_static(centers, n):
22
+ """
23
+ Computes the maximum possible radii for a given set of circle centers using
24
+ an iterative growth and constraint resolution method. This static version
25
+ incorporates an adaptive growth factor and dynamic tolerance for enhanced
26
+ performance and precision, based on the best prior implementation.
27
+
28
+ Args:
29
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
30
+ n (int): Number of circles.
31
+
32
+ Returns:
33
+ np.array: An array of shape (n) containing the final radius of each circle.
34
+ """
35
+ radii = np.zeros(n)
36
+
37
+ # Parameters for adaptive growth factor and dynamic tolerance
38
+ growth_factor_initial = 1.005
39
+ growth_factor_final = 1.002
40
+ tolerance_initial = 1e-7
41
+ tolerance_final = 1e-10
42
+
43
+ outer_iterations = 400
44
+ inner_iterations = 20
45
+
46
+ # Initialize radii based on the distance to the square's boundaries.
47
+ for i in range(n):
48
+ x, y = centers[i]
49
+ radii[i] = min(x, 1 - x, y, 1 - y)
50
+
51
+ for outer_iter_idx in range(outer_iterations):
52
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0 + 1e-9)
53
+ # Use exponential decay for smoother, more effective parameter transition, based on prior high-scoring versions.
54
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**interp_factor
55
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**interp_factor
56
+
57
+ radii *= current_growth_factor # Tentatively grow all radii
58
+
59
+ for _inner_iter_idx in range(inner_iterations):
60
+ constraints_changed = False
61
+
62
+ # Enforce boundary constraints with dynamic tolerance
63
+ for i in range(n):
64
+ x, y = centers[i]
65
+ boundary_limit = min(x, 1 - x, y, 1 - y)
66
+ if radii[i] > boundary_limit + current_tolerance:
67
+ radii[i] = boundary_limit
68
+ constraints_changed = True
69
+
70
+ # Resolve overlaps between circles with dynamic tolerance
71
+ for i in range(n):
72
+ for j in range(i + 1, n):
73
+ dist = np.linalg.norm(centers[i] - centers[j])
74
+ if radii[i] + radii[j] > dist + current_tolerance:
75
+ total_radius = radii[i] + radii[j]
76
+ if total_radius > tolerance_final:
77
+ scale = dist / total_radius
78
+ radii[i] *= scale
79
+ radii[j] *= scale
80
+ constraints_changed = True
81
+
82
+ if not constraints_changed:
83
+ break
84
+ return radii
85
+
86
+ def _initial_grid_placement(self):
87
+ """
88
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
89
+ """
90
+ coords = np.linspace(0.1, 0.9, 5)
91
+ grid_centers = np.array(list(product(coords, coords)))
92
+ self.centers[:25] = grid_centers
93
+
94
+ def _find_optimal_26th_circle_position(self):
95
+ """
96
+ Performs a multi-resolution grid search to find the best initial position
97
+ for the 26th circle among a dense set of interstitial candidates.
98
+ """
99
+ base_25_centers = np.copy(self.centers[:25])
100
+
101
+ # Initialize with a default position (center of the square) and calculate its sum of radii
102
+ optimal_26th_pos = np.array([0.5, 0.5])
103
+ trial_centers_initial = np.vstack([base_25_centers, optimal_26th_pos])
104
+ trial_radii_initial = CirclePacker._compute_max_radii_static(trial_centers_initial, self.n)
105
+ best_sum_radii = np.sum(trial_radii_initial)
106
+
107
+ # Keep track of the best position from the coarse search to center the fine search
108
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
109
+
110
+ # Expand the coarse search grid to cover a broader area, as per recommendations.
111
+ interstitial_core_coords = np.linspace(0.1, 0.9, 8)
112
+
113
+ # --- Phase 1: Coarse Grid Search ---
114
+ # Explore a broader region first to identify promising areas.
115
+ coarse_delta = 0.05
116
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta])
117
+
118
+ coarse_candidate_points = []
119
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
120
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets):
121
+ coarse_candidate_points.append([base_x + offset_x, base_y + offset_y])
122
+
123
+ for candidate_pos in coarse_candidate_points:
124
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
125
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
126
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
127
+ current_sum_radii = np.sum(trial_radii)
128
+
129
+ if current_sum_radii > best_sum_radii:
130
+ best_sum_radii = current_sum_radii
131
+ optimal_26th_pos = clipped_candidate_pos
132
+ best_coarse_pos_for_fine_tuning = clipped_candidate_pos
133
+
134
+ # --- Phase 2: Fine Grid Search around the best coarse position ---
135
+ # Focus the search more precisely around the most promising area identified in Phase 1.
136
+ fine_delta = 0.01
137
+ fine_perturbation_offsets = np.array([-fine_delta, 0, fine_delta])
138
+
139
+ fine_candidate_points = []
140
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets):
141
+ fine_candidate_points.append([best_coarse_pos_for_fine_tuning[0] + offset_x, best_coarse_pos_for_fine_tuning[1] + offset_y])
142
+
143
+ for candidate_pos in fine_candidate_points:
144
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
145
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
146
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
147
+ current_sum_radii = np.sum(trial_radii)
148
+
149
+ if current_sum_radii > best_sum_radii:
150
+ best_sum_radii = current_sum_radii
151
+ optimal_26th_pos = clipped_candidate_pos
152
+
153
+ self.centers[25] = optimal_26th_pos
154
+
155
+ return self.centers, best_sum_radii
156
+
157
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
158
+ """
159
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
160
+ positions of a cluster of circles: the 26th and its nearest neighbors.
161
+ This allows the base grid to relax and better accommodate the interstitial circle.
162
+ This version is enhanced with stress-based selection and dynamic step size adjustment.
163
+ """
164
+ current_centers = np.copy(initial_centers)
165
+ # Compute radii for initial stress calculation and consistent sum.
166
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
167
+ current_sum_radii = np.sum(current_radii)
168
+
169
+ best_centers_local = np.copy(current_centers)
170
+ best_sum_radii_local = current_sum_radii
171
+
172
+ # SA parameters adapted from high-performing prior implementations for fine-tuning
173
+ num_iterations = 150
174
+ initial_step_size = 0.005
175
+ initial_temp = 0.0001
176
+ cooling_rate = 0.99
177
+
178
+ step_size = initial_step_size
179
+ temp = initial_temp
180
+
181
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
182
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
183
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
184
+ cluster_indices = np.append(closest_neighbor_indices, 25)
185
+
186
+ # Parameters for dynamic step size adjustment
187
+ acceptance_window = 30
188
+ acceptance_count = 0
189
+ target_acceptance_rate = 0.44
190
+ adjustment_factor = 1.05
191
+
192
+ for i in range(num_iterations):
193
+ # Stress-based selection within the cluster
194
+ cluster_radii = current_radii[cluster_indices]
195
+ inv_radii = 1.0 / (cluster_radii + 1e-9)
196
+ weights = (inv_radii - np.min(inv_radii))**2
197
+ if np.sum(weights) > 1e-9:
198
+ probabilities = weights / np.sum(weights)
199
+ idx_to_move = np.random.choice(cluster_indices, p=probabilities)
200
+ else:
201
+ idx_to_move = np.random.choice(cluster_indices)
202
+
203
+
204
+ trial_centers = np.copy(current_centers)
205
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
206
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
207
+
208
+ # Evaluate the new configuration
209
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
210
+ trial_sum_radii = np.sum(trial_radii)
211
+
212
+ # Metropolis-Hastings acceptance criterion
213
+ delta_energy = trial_sum_radii - current_sum_radii
214
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
215
+ current_centers = trial_centers
216
+ current_radii = trial_radii # Update radii for stress calculation
217
+ current_sum_radii = trial_sum_radii
218
+ acceptance_count += 1
219
+
220
+ if current_sum_radii > best_sum_radii_local:
221
+ best_sum_radii_local = current_sum_radii
222
+ best_centers_local = np.copy(current_centers)
223
+
224
+ # Dynamic step size adjustment
225
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
226
+ acceptance_rate = acceptance_count / acceptance_window
227
+ if acceptance_rate > target_acceptance_rate:
228
+ step_size *= adjustment_factor
229
+ else:
230
+ step_size /= adjustment_factor
231
+ acceptance_count = 0
232
+
233
+ temp *= cooling_rate
234
+ step_size = max(step_size * cooling_rate, 1e-7)
235
+
236
+ return best_centers_local, best_sum_radii_local
237
+
238
+ def _global_refinement_sa(self, initial_centers):
239
+ """
240
+ Applies a global, low-temperature Simulated Annealing search to fine-tune
241
+ - the positions of ALL circles, acting as a "gentle jiggle" phase to
242
+ - settle the entire packing into a better local optimum. This technique was
243
+ - present in prior high-scoring implementations.
244
+ - """
245
+ - # SA parameters adapted from high-performing prior implementations for a thorough yet gentle search.
246
+ - sa_iterations = 300
247
+ + all circle positions. This version incorporates a new "cluster move" to
248
+ + perturb small, spatially connected groups of circles simultaneously,
249
+ + enhancing the ability to escape local minima in the rigid grid.
250
+ + """
251
+ + # SA parameters adapted from high-performing prior implementations.
252
+ + # Increased iterations to accommodate the new cluster move type.
253
+ + sa_iterations = 400
254
+ sa_initial_temp = 5e-6
255
+ sa_cooling_rate = 0.99
256
+ sa_initial_step_size = 0.001
257
+
258
+ + # New parameters for cluster moves
259
+ + cluster_move_prob = 0.2 # Probability of performing a cluster move
260
+ + cluster_size = 3 # Number of circles in a cluster (seed + 2 neighbors)
261
+ +
262
+ current_centers = np.copy(initial_centers)
263
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
264
+ current_sum_radii = np.sum(current_radii)
265
+
266
+ best_centers = np.copy(current_centers)
267
+ best_sum_radii = current_sum_radii
268
+
269
+ temp = sa_initial_temp
270
+ step_size = sa_initial_step_size
271
+
272
+ all_indices = np.arange(self.n)
273
+
274
+ # Parameters for dynamic step size adjustment
275
+ acceptance_window = 30
276
+ acceptance_count = 0
277
+ - target_acceptance_rate = 0.44 # Common target for SA
278
+ + target_acceptance_rate = 0.44
279
+ adjustment_factor = 1.05
280
+
281
+ for i in range(sa_iterations):
282
+ - # Stress-based selection: prioritize moving circles with smaller radii.
283
+ - inv_radii = 1.0 / (current_radii + 1e-9)
284
+ - weights = (inv_radii - np.min(inv_radii))**2
285
+ - if np.sum(weights) > 1e-9:
286
+ - probabilities = weights / np.sum(weights)
287
+ - idx_to_move = np.random.choice(all_indices, p=probabilities)
288
+ - else:
289
+ - idx_to_move = np.random.choice(all_indices)
290
+ -
291
+ trial_centers = np.copy(current_centers)
292
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
293
+ - trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
294
+ -
295
+ +
296
+ + # Decide which indices to move based on move type
297
+ + if np.random.rand() < cluster_move_prob:
298
+ + # --- Cluster Move ---
299
+ + # Select a random seed and its nearest neighbors. This helps to
300
+ + # perform coordinated shifts and escape grid-like local optima.
301
+ + seed_idx = np.random.choice(all_indices)
302
+ + distances = np.linalg.norm(current_centers - current_centers[seed_idx], axis=1)
303
+ + indices_to_move = np.argsort(distances)[:cluster_size]
304
+ + else:
305
+ + # --- Single Move (stress-based) ---
306
+ + # Prioritize moving circles with smaller radii (higher "stress").
307
+ + inv_radii = 1.0 / (current_radii + 1e-9)
308
+ + weights = (inv_radii - np.min(inv_radii))**2
309
+ + if np.sum(weights) > 1e-9:
310
+ + probabilities = weights / np.sum(weights)
311
+ + idx_to_move = np.random.choice(all_indices, p=probabilities)
312
+ + else:
313
+ + idx_to_move = np.random.choice(all_indices)
314
+ + indices_to_move = np.array([idx_to_move])
315
+ +
316
+ + # Apply the move to the selected circle(s)
317
+ + for idx in indices_to_move:
318
+ + trial_centers[idx] = np.clip(trial_centers[idx] + move, 0.0, 1.0)
319
+ +
320
+ + # --- Evaluate and accept/reject the move ---
321
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
322
+ trial_sum_radii = np.sum(trial_radii)
323
+
324
+ delta_energy = trial_sum_radii - current_sum_radii
325
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
326
+ current_centers = trial_centers
327
+ - current_radii = trial_radii # Keep radii in sync for stress calculation
328
+ + current_radii = trial_radii # Keep radii in sync for next iteration's stress calc
329
+ current_sum_radii = trial_sum_radii
330
+ acceptance_count += 1
331
+
332
+ if current_sum_radii > best_sum_radii:
333
+ best_sum_radii = current_sum_radii
334
+ best_centers = np.copy(current_centers)
335
+
336
+ - # Periodically adjust step_size based on acceptance rate to balance exploration/exploitation.
337
+ + # Periodically adjust step_size based on acceptance rate
338
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
339
+ acceptance_rate = acceptance_count / acceptance_window
340
+ if acceptance_rate > target_acceptance_rate:
341
+ step_size *= adjustment_factor
342
+ else:
343
+ step_size /= adjustment_factor
344
+ acceptance_count = 0
345
+
346
+ temp *= sa_cooling_rate
347
+ - step_size = max(step_size * sa_cooling_rate, 1e-8) # Maintain overall cooling trend for step size
348
+ + step_size = max(step_size * sa_cooling_rate, 1e-8)
349
+
350
+ return best_centers
351
+
352
+ def construct_packing(self):
353
+ """
354
+ Main method to construct the circle packing, orchestrating a multi-stage
355
+ optimization: initial placement, multi-res search, local SA, and global SA.
356
+ """
357
+ self._initial_grid_placement()
358
+
359
+ if self.n > 25:
360
+ # Stage 1: Multi-resolution search for the 26th circle's initial position
361
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
362
+
363
+ # Stage 2: Local refinement of the interstitial circle and its neighbors using SA
364
+ centers_after_local_sa, _ = self._local_refinement_cluster_sa(
365
+ centers_after_search,
366
+ sum_radii_after_search
367
+ )
368
+
369
+ # Stage 3: Global "gentle jiggle" refinement on all circles using SA
370
+ centers_after_global_sa = self._global_refinement_sa(centers_after_local_sa)
371
+ self.centers = centers_after_global_sa
372
+
373
+ # Final radius calculation for the fully optimized center configuration
374
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
375
+ return self.centers, self.radii
376
+
377
+
378
+ def construct_packing():
379
+ """
380
+ Constructs an arrangement of 26 circles by leveraging a superior three-stage
381
+ optimization strategy: initial grid, dense interstitial search, and localized SA.
382
+
383
+ Returns:
384
+ Tuple of (centers, radii)
385
+ centers: np.array of shape (26, 2) with (x, y) coordinates
386
+ radii: np.array of shape (26) with final radius of each circle
387
+ """
388
+ packer = CirclePacker(num_circles=26)
389
+ centers, radii = packer.construct_packing()
390
+ return centers, radii
391
+ # EVOLVE-BLOCK-END
392
+
393
+
394
+ # This part remains fixed (not evolved)
395
+ def run_packing():
396
+ """Run the circle packing constructor for n=26"""
397
+ centers, radii = construct_packing()
398
+ # Calculate the sum of radii
399
+ sum_radii = np.sum(radii)
400
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_106/main.py ADDED
@@ -0,0 +1,376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings within a unit square using a hybrid
9
+ optimization approach. It combines an exhaustive search for initial placement
10
+ with a localized refinement stage.
11
+ """
12
+ def __init__(self, num_circles=26):
13
+ self.n = num_circles
14
+ self.centers = np.zeros((self.n, 2))
15
+ self.radii = np.zeros(self.n)
16
+
17
+ @staticmethod
18
+ def _compute_max_radii_static(centers, n):
19
+ """
20
+ Computes the maximum possible radii for a given set of circle centers using
21
+ an iterative growth and constraint resolution method. This static version
22
+ incorporates an adaptive growth factor and dynamic tolerance for enhanced
23
+ performance and precision, based on the best prior implementation.
24
+
25
+ Args:
26
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
27
+ n (int): Number of circles.
28
+
29
+ Returns:
30
+ np.array: An array of shape (n) containing the final radius of each circle.
31
+ """
32
+ radii = np.zeros(n)
33
+
34
+ # Parameters for adaptive growth factor and dynamic tolerance
35
+ growth_factor_initial = 1.005
36
+ growth_factor_final = 1.002
37
+ tolerance_initial = 1e-7
38
+ tolerance_final = 1e-10
39
+
40
+ outer_iterations = 400
41
+ inner_iterations = 20
42
+
43
+ # Initialize radii based on the distance to the square's boundaries.
44
+ for i in range(n):
45
+ x, y = centers[i]
46
+ radii[i] = min(x, 1 - x, y, 1 - y)
47
+
48
+ for outer_iter_idx in range(outer_iterations):
49
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0 + 1e-9)
50
+ # Use exponential decay for smoother, more effective parameter transition, based on prior high-scoring versions.
51
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**interp_factor
52
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**interp_factor
53
+
54
+ radii *= current_growth_factor # Tentatively grow all radii
55
+
56
+ for _inner_iter_idx in range(inner_iterations):
57
+ constraints_changed = False
58
+
59
+ # Enforce boundary constraints with dynamic tolerance
60
+ for i in range(n):
61
+ x, y = centers[i]
62
+ boundary_limit = min(x, 1 - x, y, 1 - y)
63
+ if radii[i] > boundary_limit + current_tolerance:
64
+ radii[i] = boundary_limit
65
+ constraints_changed = True
66
+
67
+ # Resolve overlaps between circles with dynamic tolerance
68
+ for i in range(n):
69
+ for j in range(i + 1, n):
70
+ dist = np.linalg.norm(centers[i] - centers[j])
71
+ if radii[i] + radii[j] > dist + current_tolerance:
72
+ total_radius = radii[i] + radii[j]
73
+ if total_radius > tolerance_final:
74
+ scale = dist / total_radius
75
+ radii[i] *= scale
76
+ radii[j] *= scale
77
+ constraints_changed = True
78
+
79
+ if not constraints_changed:
80
+ break
81
+ return radii
82
+
83
+ def _initial_grid_placement(self):
84
+ """
85
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
86
+ """
87
+ coords = np.linspace(0.1, 0.9, 5)
88
+ grid_centers = np.array(list(product(coords, coords)))
89
+ self.centers[:25] = grid_centers
90
+
91
+ def _find_optimal_26th_circle_position(self):
92
+ """
93
+ Performs a multi-resolution grid search to find the best initial position
94
+ for the 26th circle among a dense set of interstitial candidates.
95
+ """
96
+ base_25_centers = np.copy(self.centers[:25])
97
+
98
+ # Initialize with a default position (center of the square) and calculate its sum of radii
99
+ optimal_26th_pos = np.array([0.5, 0.5])
100
+ trial_centers_initial = np.vstack([base_25_centers, optimal_26th_pos])
101
+ trial_radii_initial = CirclePacker._compute_max_radii_static(trial_centers_initial, self.n)
102
+ best_sum_radii = np.sum(trial_radii_initial)
103
+
104
+ # Keep track of the best position from the coarse search to center the fine search
105
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
106
+
107
+ # Expand the coarse search grid to cover a broader area, as per recommendations.
108
+ interstitial_core_coords = np.linspace(0.1, 0.9, 8)
109
+
110
+ # --- Phase 1: Coarse Grid Search ---
111
+ # Explore a broader region first to identify promising areas.
112
+ coarse_delta = 0.05
113
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta])
114
+
115
+ coarse_candidate_points = []
116
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
117
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets):
118
+ coarse_candidate_points.append([base_x + offset_x, base_y + offset_y])
119
+
120
+ for candidate_pos in coarse_candidate_points:
121
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
122
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
123
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
124
+ current_sum_radii = np.sum(trial_radii)
125
+
126
+ if current_sum_radii > best_sum_radii:
127
+ best_sum_radii = current_sum_radii
128
+ optimal_26th_pos = clipped_candidate_pos
129
+ best_coarse_pos_for_fine_tuning = clipped_candidate_pos
130
+
131
+ # --- Phase 2: Fine Grid Search around the best coarse position ---
132
+ # Focus the search more precisely around the most promising area identified in Phase 1.
133
+ fine_delta = 0.01
134
+ fine_perturbation_offsets = np.array([-fine_delta, 0, fine_delta])
135
+
136
+ fine_candidate_points = []
137
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets):
138
+ fine_candidate_points.append([best_coarse_pos_for_fine_tuning[0] + offset_x, best_coarse_pos_for_fine_tuning[1] + offset_y])
139
+
140
+ for candidate_pos in fine_candidate_points:
141
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
142
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
143
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
144
+ current_sum_radii = np.sum(trial_radii)
145
+
146
+ if current_sum_radii > best_sum_radii:
147
+ best_sum_radii = current_sum_radii
148
+ optimal_26th_pos = clipped_candidate_pos
149
+
150
+ self.centers[25] = optimal_26th_pos
151
+
152
+ return self.centers, best_sum_radii
153
+
154
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
155
+ """
156
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
157
+ positions of a cluster of circles: the 26th and its nearest neighbors.
158
+ This allows the base grid to relax and better accommodate the interstitial circle.
159
+ This version is enhanced with stress-based selection and dynamic step size adjustment.
160
+ """
161
+ current_centers = np.copy(initial_centers)
162
+ # Compute radii for initial stress calculation and consistent sum.
163
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
164
+ current_sum_radii = np.sum(current_radii)
165
+
166
+ best_centers_local = np.copy(current_centers)
167
+ best_sum_radii_local = current_sum_radii
168
+
169
+ # SA parameters adapted from high-performing prior implementations for fine-tuning
170
+ num_iterations = 150
171
+ initial_step_size = 0.005
172
+ initial_temp = 0.0001
173
+ cooling_rate = 0.99
174
+
175
+ step_size = initial_step_size
176
+ temp = initial_temp
177
+
178
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
179
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
180
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
181
+ cluster_indices = np.append(closest_neighbor_indices, 25)
182
+
183
+ # Parameters for dynamic step size adjustment
184
+ acceptance_window = 30
185
+ acceptance_count = 0
186
+ target_acceptance_rate = 0.44
187
+ adjustment_factor = 1.05
188
+
189
+ for i in range(num_iterations):
190
+ # Stress-based selection within the cluster
191
+ cluster_radii = current_radii[cluster_indices]
192
+ inv_radii = 1.0 / (cluster_radii + 1e-9)
193
+ weights = (inv_radii - np.min(inv_radii))**2
194
+ if np.sum(weights) > 1e-9:
195
+ probabilities = weights / np.sum(weights)
196
+ idx_to_move = np.random.choice(cluster_indices, p=probabilities)
197
+ else:
198
+ idx_to_move = np.random.choice(cluster_indices)
199
+
200
+
201
+ trial_centers = np.copy(current_centers)
202
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
203
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
204
+
205
+ # Evaluate the new configuration
206
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
207
+ trial_sum_radii = np.sum(trial_radii)
208
+
209
+ # Metropolis-Hastings acceptance criterion
210
+ delta_energy = trial_sum_radii - current_sum_radii
211
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
212
+ current_centers = trial_centers
213
+ current_radii = trial_radii # Update radii for stress calculation
214
+ current_sum_radii = trial_sum_radii
215
+ acceptance_count += 1
216
+
217
+ if current_sum_radii > best_sum_radii_local:
218
+ best_sum_radii_local = current_sum_radii
219
+ best_centers_local = np.copy(current_centers)
220
+
221
+ # Dynamic step size adjustment
222
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
223
+ acceptance_rate = acceptance_count / acceptance_window
224
+ if acceptance_rate > target_acceptance_rate:
225
+ step_size *= adjustment_factor
226
+ else:
227
+ step_size /= adjustment_factor
228
+ acceptance_count = 0
229
+
230
+ temp *= cooling_rate
231
+ step_size = max(step_size * cooling_rate, 1e-7)
232
+
233
+ return best_centers_local, best_sum_radii_local
234
+
235
+ def _global_refinement_sa(self, initial_centers):
236
+ """
237
+ Applies a global, low-temperature Simulated Annealing search to fine-tune
238
+ all circle positions. This version incorporates a new "cluster move" to
239
+ perturb small, spatially connected groups of circles simultaneously,
240
+ enhancing the ability to escape local minima in the rigid grid.
241
+ """
242
+ # SA parameters adapted from high-performing prior implementations.
243
+ # Increased iterations to accommodate the new cluster move type.
244
+ sa_iterations = 400
245
+ sa_initial_temp = 5e-6
246
+ sa_cooling_rate = 0.99
247
+ sa_initial_step_size = 0.001
248
+
249
+ # New parameters for cluster moves
250
+ cluster_move_prob = 0.2 # Probability of performing a cluster move
251
+ cluster_size = 3 # Number of circles in a cluster (seed + 2 neighbors)
252
+
253
+ current_centers = np.copy(initial_centers)
254
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
255
+ current_sum_radii = np.sum(current_radii)
256
+
257
+ best_centers = np.copy(current_centers)
258
+ best_sum_radii = current_sum_radii
259
+
260
+ temp = sa_initial_temp
261
+ step_size = sa_initial_step_size
262
+
263
+ all_indices = np.arange(self.n)
264
+
265
+ # Parameters for dynamic step size adjustment
266
+ acceptance_window = 30
267
+ acceptance_count = 0
268
+ target_acceptance_rate = 0.44
269
+ adjustment_factor = 1.05
270
+
271
+ for i in range(sa_iterations):
272
+ trial_centers = np.copy(current_centers)
273
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
274
+
275
+ # Decide which indices to move based on move type
276
+ if np.random.rand() < cluster_move_prob:
277
+ # --- Cluster Move ---
278
+ # Select a random seed and its nearest neighbors. This helps to
279
+ # perform coordinated shifts and escape grid-like local optima.
280
+ seed_idx = np.random.choice(all_indices)
281
+ distances = np.linalg.norm(current_centers - current_centers[seed_idx], axis=1)
282
+ indices_to_move = np.argsort(distances)[:cluster_size]
283
+ else:
284
+ # --- Single Move (stress-based) ---
285
+ # Prioritize moving circles with smaller radii (higher "stress").
286
+ inv_radii = 1.0 / (current_radii + 1e-9)
287
+ weights = (inv_radii - np.min(inv_radii))**2
288
+ if np.sum(weights) > 1e-9:
289
+ probabilities = weights / np.sum(weights)
290
+ idx_to_move = np.random.choice(all_indices, p=probabilities)
291
+ else:
292
+ idx_to_move = np.random.choice(all_indices)
293
+ indices_to_move = np.array([idx_to_move])
294
+
295
+ # Apply the move to the selected circle(s)
296
+ for idx in indices_to_move:
297
+ trial_centers[idx] = np.clip(trial_centers[idx] + move, 0.0, 1.0)
298
+
299
+ # --- Evaluate and accept/reject the move ---
300
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
301
+ trial_sum_radii = np.sum(trial_radii)
302
+
303
+ delta_energy = trial_sum_radii - current_sum_radii
304
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
305
+ current_centers = trial_centers
306
+ current_radii = trial_radii # Keep radii in sync for next iteration's stress calc
307
+ current_sum_radii = trial_sum_radii
308
+ acceptance_count += 1
309
+
310
+ if current_sum_radii > best_sum_radii:
311
+ best_sum_radii = current_sum_radii
312
+ best_centers = np.copy(current_centers)
313
+
314
+ # Periodically adjust step_size based on acceptance rate
315
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
316
+ acceptance_rate = acceptance_count / acceptance_window
317
+ if acceptance_rate > target_acceptance_rate:
318
+ step_size *= adjustment_factor
319
+ else:
320
+ step_size /= adjustment_factor
321
+ acceptance_count = 0
322
+
323
+ temp *= sa_cooling_rate
324
+ step_size = max(step_size * sa_cooling_rate, 1e-8)
325
+
326
+ return best_centers
327
+
328
+ def construct_packing(self):
329
+ """
330
+ Main method to construct the circle packing, orchestrating a multi-stage
331
+ optimization: initial placement, multi-res search, local SA, and global SA.
332
+ """
333
+ self._initial_grid_placement()
334
+
335
+ if self.n > 25:
336
+ # Stage 1: Multi-resolution search for the 26th circle's initial position
337
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
338
+
339
+ # Stage 2: Local refinement of the interstitial circle and its neighbors using SA
340
+ centers_after_local_sa, _ = self._local_refinement_cluster_sa(
341
+ centers_after_search,
342
+ sum_radii_after_search
343
+ )
344
+
345
+ # Stage 3: Global "gentle jiggle" refinement on all circles using SA
346
+ centers_after_global_sa = self._global_refinement_sa(centers_after_local_sa)
347
+ self.centers = centers_after_global_sa
348
+
349
+ # Final radius calculation for the fully optimized center configuration
350
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
351
+ return self.centers, self.radii
352
+
353
+
354
+ def construct_packing():
355
+ """
356
+ Constructs an arrangement of 26 circles by leveraging a superior three-stage
357
+ optimization strategy: initial grid, dense interstitial search, and localized SA.
358
+
359
+ Returns:
360
+ Tuple of (centers, radii)
361
+ centers: np.array of shape (26, 2) with (x, y) coordinates
362
+ radii: np.array of shape (26) with final radius of each circle
363
+ """
364
+ packer = CirclePacker(num_circles=26)
365
+ centers, radii = packer.construct_packing()
366
+ return centers, radii
367
+ # EVOLVE-BLOCK-END
368
+
369
+
370
+ # This part remains fixed (not evolved)
371
+ def run_packing():
372
+ """Run the circle packing constructor for n=26"""
373
+ centers, radii = construct_packing()
374
+ # Calculate the sum of radii
375
+ sum_radii = np.sum(radii)
376
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_106/original.py ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from itertools import product
4
+
5
+
6
+ class CirclePacker:
7
+ """
8
+ A class to construct circle packings within a unit square using a hybrid
9
+ optimization approach. It combines an exhaustive search for initial placement
10
+ with a localized refinement stage.
11
+ """
12
+ def __init__(self, num_circles=26):
13
+ self.n = num_circles
14
+ self.centers = np.zeros((self.n, 2))
15
+ self.radii = np.zeros(self.n)
16
+
17
+ @staticmethod
18
+ def _compute_max_radii_static(centers, n):
19
+ """
20
+ Computes the maximum possible radii for a given set of circle centers using
21
+ an iterative growth and constraint resolution method. This static version
22
+ incorporates an adaptive growth factor and dynamic tolerance for enhanced
23
+ performance and precision, based on the best prior implementation.
24
+
25
+ Args:
26
+ centers (np.array): An array of shape (n, 2) with (x, y) coordinates.
27
+ n (int): Number of circles.
28
+
29
+ Returns:
30
+ np.array: An array of shape (n) containing the final radius of each circle.
31
+ """
32
+ radii = np.zeros(n)
33
+
34
+ # Parameters for adaptive growth factor and dynamic tolerance
35
+ growth_factor_initial = 1.005
36
+ growth_factor_final = 1.002
37
+ tolerance_initial = 1e-7
38
+ tolerance_final = 1e-10
39
+
40
+ outer_iterations = 400
41
+ inner_iterations = 20
42
+
43
+ # Initialize radii based on the distance to the square's boundaries.
44
+ for i in range(n):
45
+ x, y = centers[i]
46
+ radii[i] = min(x, 1 - x, y, 1 - y)
47
+
48
+ for outer_iter_idx in range(outer_iterations):
49
+ interp_factor = outer_iter_idx / (outer_iterations - 1.0 + 1e-9)
50
+ # Use exponential decay for smoother, more effective parameter transition, based on prior high-scoring versions.
51
+ current_growth_factor = growth_factor_initial * (growth_factor_final / growth_factor_initial)**interp_factor
52
+ current_tolerance = tolerance_initial * (tolerance_final / tolerance_initial)**interp_factor
53
+
54
+ radii *= current_growth_factor # Tentatively grow all radii
55
+
56
+ for _inner_iter_idx in range(inner_iterations):
57
+ constraints_changed = False
58
+
59
+ # Enforce boundary constraints with dynamic tolerance
60
+ for i in range(n):
61
+ x, y = centers[i]
62
+ boundary_limit = min(x, 1 - x, y, 1 - y)
63
+ if radii[i] > boundary_limit + current_tolerance:
64
+ radii[i] = boundary_limit
65
+ constraints_changed = True
66
+
67
+ # Resolve overlaps between circles with dynamic tolerance
68
+ for i in range(n):
69
+ for j in range(i + 1, n):
70
+ dist = np.linalg.norm(centers[i] - centers[j])
71
+ if radii[i] + radii[j] > dist + current_tolerance:
72
+ total_radius = radii[i] + radii[j]
73
+ if total_radius > tolerance_final:
74
+ scale = dist / total_radius
75
+ radii[i] *= scale
76
+ radii[j] *= scale
77
+ constraints_changed = True
78
+
79
+ if not constraints_changed:
80
+ break
81
+ return radii
82
+
83
+ def _initial_grid_placement(self):
84
+ """
85
+ Places the first 25 circles in a 5x5 grid pattern within the unit square.
86
+ """
87
+ coords = np.linspace(0.1, 0.9, 5)
88
+ grid_centers = np.array(list(product(coords, coords)))
89
+ self.centers[:25] = grid_centers
90
+
91
+ def _find_optimal_26th_circle_position(self):
92
+ """
93
+ Performs a multi-resolution grid search to find the best initial position
94
+ for the 26th circle among a dense set of interstitial candidates.
95
+ """
96
+ base_25_centers = np.copy(self.centers[:25])
97
+
98
+ # Initialize with a default position (center of the square) and calculate its sum of radii
99
+ optimal_26th_pos = np.array([0.5, 0.5])
100
+ trial_centers_initial = np.vstack([base_25_centers, optimal_26th_pos])
101
+ trial_radii_initial = CirclePacker._compute_max_radii_static(trial_centers_initial, self.n)
102
+ best_sum_radii = np.sum(trial_radii_initial)
103
+
104
+ # Keep track of the best position from the coarse search to center the fine search
105
+ best_coarse_pos_for_fine_tuning = optimal_26th_pos
106
+
107
+ # Expand the coarse search grid to cover a broader area, as per recommendations.
108
+ interstitial_core_coords = np.linspace(0.1, 0.9, 8)
109
+
110
+ # --- Phase 1: Coarse Grid Search ---
111
+ # Explore a broader region first to identify promising areas.
112
+ coarse_delta = 0.05
113
+ coarse_perturbation_offsets = np.array([-coarse_delta, 0, coarse_delta])
114
+
115
+ coarse_candidate_points = []
116
+ for base_x, base_y in product(interstitial_core_coords, interstitial_core_coords):
117
+ for offset_x, offset_y in product(coarse_perturbation_offsets, coarse_perturbation_offsets):
118
+ coarse_candidate_points.append([base_x + offset_x, base_y + offset_y])
119
+
120
+ for candidate_pos in coarse_candidate_points:
121
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
122
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
123
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
124
+ current_sum_radii = np.sum(trial_radii)
125
+
126
+ if current_sum_radii > best_sum_radii:
127
+ best_sum_radii = current_sum_radii
128
+ optimal_26th_pos = clipped_candidate_pos
129
+ best_coarse_pos_for_fine_tuning = clipped_candidate_pos
130
+
131
+ # --- Phase 2: Fine Grid Search around the best coarse position ---
132
+ # Focus the search more precisely around the most promising area identified in Phase 1.
133
+ fine_delta = 0.01
134
+ fine_perturbation_offsets = np.array([-fine_delta, 0, fine_delta])
135
+
136
+ fine_candidate_points = []
137
+ for offset_x, offset_y in product(fine_perturbation_offsets, fine_perturbation_offsets):
138
+ fine_candidate_points.append([best_coarse_pos_for_fine_tuning[0] + offset_x, best_coarse_pos_for_fine_tuning[1] + offset_y])
139
+
140
+ for candidate_pos in fine_candidate_points:
141
+ clipped_candidate_pos = np.clip(candidate_pos, 0.0, 1.0)
142
+ trial_centers = np.vstack([base_25_centers, clipped_candidate_pos])
143
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
144
+ current_sum_radii = np.sum(trial_radii)
145
+
146
+ if current_sum_radii > best_sum_radii:
147
+ best_sum_radii = current_sum_radii
148
+ optimal_26th_pos = clipped_candidate_pos
149
+
150
+ self.centers[25] = optimal_26th_pos
151
+
152
+ return self.centers, best_sum_radii
153
+
154
+ def _local_refinement_cluster_sa(self, initial_centers, initial_sum_radii):
155
+ """
156
+ Applies a localized Simulated Annealing (SA) search to fine-tune the
157
+ positions of a cluster of circles: the 26th and its nearest neighbors.
158
+ This allows the base grid to relax and better accommodate the interstitial circle.
159
+ This version is enhanced with stress-based selection and dynamic step size adjustment.
160
+ """
161
+ current_centers = np.copy(initial_centers)
162
+ # Compute radii for initial stress calculation and consistent sum.
163
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
164
+ current_sum_radii = np.sum(current_radii)
165
+
166
+ best_centers_local = np.copy(current_centers)
167
+ best_sum_radii_local = current_sum_radii
168
+
169
+ # SA parameters adapted from high-performing prior implementations for fine-tuning
170
+ num_iterations = 150
171
+ initial_step_size = 0.005
172
+ initial_temp = 0.0001
173
+ cooling_rate = 0.99
174
+
175
+ step_size = initial_step_size
176
+ temp = initial_temp
177
+
178
+ # Identify the cluster: the 26th circle and its 4 closest neighbors.
179
+ distances_to_26th = np.linalg.norm(initial_centers[25] - initial_centers[:25], axis=1)
180
+ closest_neighbor_indices = np.argsort(distances_to_26th)[:4]
181
+ cluster_indices = np.append(closest_neighbor_indices, 25)
182
+
183
+ # Parameters for dynamic step size adjustment
184
+ acceptance_window = 30
185
+ acceptance_count = 0
186
+ target_acceptance_rate = 0.44
187
+ adjustment_factor = 1.05
188
+
189
+ for i in range(num_iterations):
190
+ # Stress-based selection within the cluster
191
+ cluster_radii = current_radii[cluster_indices]
192
+ inv_radii = 1.0 / (cluster_radii + 1e-9)
193
+ weights = (inv_radii - np.min(inv_radii))**2
194
+ if np.sum(weights) > 1e-9:
195
+ probabilities = weights / np.sum(weights)
196
+ idx_to_move = np.random.choice(cluster_indices, p=probabilities)
197
+ else:
198
+ idx_to_move = np.random.choice(cluster_indices)
199
+
200
+
201
+ trial_centers = np.copy(current_centers)
202
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
203
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
204
+
205
+ # Evaluate the new configuration
206
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
207
+ trial_sum_radii = np.sum(trial_radii)
208
+
209
+ # Metropolis-Hastings acceptance criterion
210
+ delta_energy = trial_sum_radii - current_sum_radii
211
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
212
+ current_centers = trial_centers
213
+ current_radii = trial_radii # Update radii for stress calculation
214
+ current_sum_radii = trial_sum_radii
215
+ acceptance_count += 1
216
+
217
+ if current_sum_radii > best_sum_radii_local:
218
+ best_sum_radii_local = current_sum_radii
219
+ best_centers_local = np.copy(current_centers)
220
+
221
+ # Dynamic step size adjustment
222
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
223
+ acceptance_rate = acceptance_count / acceptance_window
224
+ if acceptance_rate > target_acceptance_rate:
225
+ step_size *= adjustment_factor
226
+ else:
227
+ step_size /= adjustment_factor
228
+ acceptance_count = 0
229
+
230
+ temp *= cooling_rate
231
+ step_size = max(step_size * cooling_rate, 1e-7)
232
+
233
+ return best_centers_local, best_sum_radii_local
234
+
235
+ def _global_refinement_sa(self, initial_centers):
236
+ """
237
+ Applies a global, low-temperature Simulated Annealing search to fine-tune
238
+ the positions of ALL circles, acting as a "gentle jiggle" phase to
239
+ settle the entire packing into a better local optimum. This technique was
240
+ present in prior high-scoring implementations.
241
+ """
242
+ # SA parameters adapted from high-performing prior implementations for a thorough yet gentle search.
243
+ sa_iterations = 300
244
+ sa_initial_temp = 5e-6
245
+ sa_cooling_rate = 0.99
246
+ sa_initial_step_size = 0.001
247
+
248
+ current_centers = np.copy(initial_centers)
249
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
250
+ current_sum_radii = np.sum(current_radii)
251
+
252
+ best_centers = np.copy(current_centers)
253
+ best_sum_radii = current_sum_radii
254
+
255
+ temp = sa_initial_temp
256
+ step_size = sa_initial_step_size
257
+
258
+ all_indices = np.arange(self.n)
259
+
260
+ # Parameters for dynamic step size adjustment
261
+ acceptance_window = 30
262
+ acceptance_count = 0
263
+ target_acceptance_rate = 0.44 # Common target for SA
264
+ adjustment_factor = 1.05
265
+
266
+ for i in range(sa_iterations):
267
+ # Stress-based selection: prioritize moving circles with smaller radii.
268
+ inv_radii = 1.0 / (current_radii + 1e-9)
269
+ weights = (inv_radii - np.min(inv_radii))**2
270
+ if np.sum(weights) > 1e-9:
271
+ probabilities = weights / np.sum(weights)
272
+ idx_to_move = np.random.choice(all_indices, p=probabilities)
273
+ else:
274
+ idx_to_move = np.random.choice(all_indices)
275
+
276
+ trial_centers = np.copy(current_centers)
277
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
278
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
279
+
280
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
281
+ trial_sum_radii = np.sum(trial_radii)
282
+
283
+ delta_energy = trial_sum_radii - current_sum_radii
284
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
285
+ current_centers = trial_centers
286
+ current_radii = trial_radii # Keep radii in sync for stress calculation
287
+ current_sum_radii = trial_sum_radii
288
+ acceptance_count += 1
289
+
290
+ if current_sum_radii > best_sum_radii:
291
+ best_sum_radii = current_sum_radii
292
+ best_centers = np.copy(current_centers)
293
+
294
+ # Periodically adjust step_size based on acceptance rate to balance exploration/exploitation.
295
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
296
+ acceptance_rate = acceptance_count / acceptance_window
297
+ if acceptance_rate > target_acceptance_rate:
298
+ step_size *= adjustment_factor
299
+ else:
300
+ step_size /= adjustment_factor
301
+ acceptance_count = 0
302
+
303
+ temp *= sa_cooling_rate
304
+ step_size = max(step_size * sa_cooling_rate, 1e-8) # Maintain overall cooling trend for step size
305
+
306
+ return best_centers
307
+
308
+ def construct_packing(self):
309
+ """
310
+ Main method to construct the circle packing, orchestrating a multi-stage
311
+ optimization: initial placement, multi-res search, local SA, and global SA.
312
+ """
313
+ self._initial_grid_placement()
314
+
315
+ if self.n > 25:
316
+ # Stage 1: Multi-resolution search for the 26th circle's initial position
317
+ centers_after_search, sum_radii_after_search = self._find_optimal_26th_circle_position()
318
+
319
+ # Stage 2: Local refinement of the interstitial circle and its neighbors using SA
320
+ centers_after_local_sa, _ = self._local_refinement_cluster_sa(
321
+ centers_after_search,
322
+ sum_radii_after_search
323
+ )
324
+
325
+ # Stage 3: Global "gentle jiggle" refinement on all circles using SA
326
+ centers_after_global_sa = self._global_refinement_sa(centers_after_local_sa)
327
+ self.centers = centers_after_global_sa
328
+
329
+ # Final radius calculation for the fully optimized center configuration
330
+ self.radii = CirclePacker._compute_max_radii_static(self.centers, self.n)
331
+ return self.centers, self.radii
332
+
333
+
334
+ def construct_packing():
335
+ """
336
+ Constructs an arrangement of 26 circles by leveraging a superior three-stage
337
+ optimization strategy: initial grid, dense interstitial search, and localized SA.
338
+
339
+ Returns:
340
+ Tuple of (centers, radii)
341
+ centers: np.array of shape (26, 2) with (x, y) coordinates
342
+ radii: np.array of shape (26) with final radius of each circle
343
+ """
344
+ packer = CirclePacker(num_circles=26)
345
+ centers, radii = packer.construct_packing()
346
+ return centers, radii
347
+ # EVOLVE-BLOCK-END
348
+
349
+
350
+ # This part remains fixed (not evolved)
351
+ def run_packing():
352
+ """Run the circle packing constructor for n=26"""
353
+ centers, radii = construct_packing()
354
+ # Calculate the sum of radii
355
+ sum_radii = np.sum(radii)
356
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results__debug/results_circle_packing_NO_vision_WITH_refined_aux_20260118_205215/gen_106/search_replace.txt ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <NAME>
2
+ cluster_moves_in_global_sa
3
+ </NAME>
4
+
5
+ <DESCRIPTION>
6
+ This edit introduces "cluster moves" into the global simulated annealing stage (`_global_refinement_sa`). With a 20% probability at each step, instead of moving a single circle, the algorithm now moves a small cluster of 3 spatially adjacent circles (a random seed circle and its two nearest neighbors) with the same translation vector.
7
+
8
+ This is a powerful exploration strategy designed to overcome the rigidity of the initial 5x5 grid packing, allowing for coordinated shifts that can unlock superior configurations and escape local optima that single-circle moves struggle with.
9
+
10
+ To support this more complex search, the number of iterations in the global SA stage has been increased from 300 to 400, providing more time for this enhanced exploration to converge on a better solution. This change directly implements a key recommendation to perturb spatially connected clusters, targeting a known weakness of single-particle optimization in highly structured problems.
11
+ </DESCRIPTION>
12
+
13
+ <DIFF>
14
+ <<<<<<< SEARCH
15
+ def _global_refinement_sa(self, initial_centers):
16
+ """
17
+ Applies a global, low-temperature Simulated Annealing search to fine-tune
18
+ the positions of ALL circles, acting as a "gentle jiggle" phase to
19
+ settle the entire packing into a better local optimum. This technique was
20
+ present in prior high-scoring implementations.
21
+ """
22
+ # SA parameters adapted from high-performing prior implementations for a thorough yet gentle search.
23
+ sa_iterations = 300
24
+ sa_initial_temp = 5e-6
25
+ sa_cooling_rate = 0.99
26
+ sa_initial_step_size = 0.001
27
+
28
+ current_centers = np.copy(initial_centers)
29
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
30
+ current_sum_radii = np.sum(current_radii)
31
+
32
+ best_centers = np.copy(current_centers)
33
+ best_sum_radii = current_sum_radii
34
+
35
+ temp = sa_initial_temp
36
+ step_size = sa_initial_step_size
37
+
38
+ all_indices = np.arange(self.n)
39
+
40
+ # Parameters for dynamic step size adjustment
41
+ acceptance_window = 30
42
+ acceptance_count = 0
43
+ target_acceptance_rate = 0.44 # Common target for SA
44
+ adjustment_factor = 1.05
45
+
46
+ for i in range(sa_iterations):
47
+ # Stress-based selection: prioritize moving circles with smaller radii.
48
+ inv_radii = 1.0 / (current_radii + 1e-9)
49
+ weights = (inv_radii - np.min(inv_radii))**2
50
+ if np.sum(weights) > 1e-9:
51
+ probabilities = weights / np.sum(weights)
52
+ idx_to_move = np.random.choice(all_indices, p=probabilities)
53
+ else:
54
+ idx_to_move = np.random.choice(all_indices)
55
+
56
+ trial_centers = np.copy(current_centers)
57
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
58
+ trial_centers[idx_to_move] = np.clip(trial_centers[idx_to_move] + move, 0.0, 1.0)
59
+
60
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
61
+ trial_sum_radii = np.sum(trial_radii)
62
+
63
+ delta_energy = trial_sum_radii - current_sum_radii
64
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
65
+ current_centers = trial_centers
66
+ current_radii = trial_radii # Keep radii in sync for stress calculation
67
+ current_sum_radii = trial_sum_radii
68
+ acceptance_count += 1
69
+
70
+ if current_sum_radii > best_sum_radii:
71
+ best_sum_radii = current_sum_radii
72
+ best_centers = np.copy(current_centers)
73
+
74
+ # Periodically adjust step_size based on acceptance rate to balance exploration/exploitation.
75
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
76
+ acceptance_rate = acceptance_count / acceptance_window
77
+ if acceptance_rate > target_acceptance_rate:
78
+ step_size *= adjustment_factor
79
+ else:
80
+ step_size /= adjustment_factor
81
+ acceptance_count = 0
82
+
83
+ temp *= sa_cooling_rate
84
+ step_size = max(step_size * sa_cooling_rate, 1e-8) # Maintain overall cooling trend for step size
85
+
86
+ return best_centers
87
+ =======
88
+ def _global_refinement_sa(self, initial_centers):
89
+ """
90
+ Applies a global, low-temperature Simulated Annealing search to fine-tune
91
+ all circle positions. This version incorporates a new "cluster move" to
92
+ perturb small, spatially connected groups of circles simultaneously,
93
+ enhancing the ability to escape local minima in the rigid grid.
94
+ """
95
+ # SA parameters adapted from high-performing prior implementations.
96
+ # Increased iterations to accommodate the new cluster move type.
97
+ sa_iterations = 400
98
+ sa_initial_temp = 5e-6
99
+ sa_cooling_rate = 0.99
100
+ sa_initial_step_size = 0.001
101
+
102
+ # New parameters for cluster moves
103
+ cluster_move_prob = 0.2 # Probability of performing a cluster move
104
+ cluster_size = 3 # Number of circles in a cluster (seed + 2 neighbors)
105
+
106
+ current_centers = np.copy(initial_centers)
107
+ current_radii = CirclePacker._compute_max_radii_static(current_centers, self.n)
108
+ current_sum_radii = np.sum(current_radii)
109
+
110
+ best_centers = np.copy(current_centers)
111
+ best_sum_radii = current_sum_radii
112
+
113
+ temp = sa_initial_temp
114
+ step_size = sa_initial_step_size
115
+
116
+ all_indices = np.arange(self.n)
117
+
118
+ # Parameters for dynamic step size adjustment
119
+ acceptance_window = 30
120
+ acceptance_count = 0
121
+ target_acceptance_rate = 0.44
122
+ adjustment_factor = 1.05
123
+
124
+ for i in range(sa_iterations):
125
+ trial_centers = np.copy(current_centers)
126
+ move = (np.random.rand(2) - 0.5) * 2 * step_size
127
+
128
+ # Decide which indices to move based on move type
129
+ if np.random.rand() < cluster_move_prob:
130
+ # --- Cluster Move ---
131
+ # Select a random seed and its nearest neighbors. This helps to
132
+ # perform coordinated shifts and escape grid-like local optima.
133
+ seed_idx = np.random.choice(all_indices)
134
+ distances = np.linalg.norm(current_centers - current_centers[seed_idx], axis=1)
135
+ indices_to_move = np.argsort(distances)[:cluster_size]
136
+ else:
137
+ # --- Single Move (stress-based) ---
138
+ # Prioritize moving circles with smaller radii (higher "stress").
139
+ inv_radii = 1.0 / (current_radii + 1e-9)
140
+ weights = (inv_radii - np.min(inv_radii))**2
141
+ if np.sum(weights) > 1e-9:
142
+ probabilities = weights / np.sum(weights)
143
+ idx_to_move = np.random.choice(all_indices, p=probabilities)
144
+ else:
145
+ idx_to_move = np.random.choice(all_indices)
146
+ indices_to_move = np.array([idx_to_move])
147
+
148
+ # Apply the move to the selected circle(s)
149
+ for idx in indices_to_move:
150
+ trial_centers[idx] = np.clip(trial_centers[idx] + move, 0.0, 1.0)
151
+
152
+ # --- Evaluate and accept/reject the move ---
153
+ trial_radii = CirclePacker._compute_max_radii_static(trial_centers, self.n)
154
+ trial_sum_radii = np.sum(trial_radii)
155
+
156
+ delta_energy = trial_sum_radii - current_sum_radii
157
+ if delta_energy > 0 or (temp > 1e-9 and np.random.rand() < np.exp(delta_energy / temp)):
158
+ current_centers = trial_centers
159
+ current_radii = trial_radii # Keep radii in sync for next iteration's stress calc
160
+ current_sum_radii = trial_sum_radii
161
+ acceptance_count += 1
162
+
163
+ if current_sum_radii > best_sum_radii:
164
+ best_sum_radii = current_sum_radii
165
+ best_centers = np.copy(current_centers)
166
+
167
+ # Periodically adjust step_size based on acceptance rate
168
+ if (i + 1) % acceptance_window == 0 and acceptance_window > 0:
169
+ acceptance_rate = acceptance_count / acceptance_window
170
+ if acceptance_rate > target_acceptance_rate:
171
+ step_size *= adjustment_factor
172
+ else:
173
+ step_size /= adjustment_factor
174
+ acceptance_count = 0
175
+
176
+ temp *= sa_cooling_rate
177
+ step_size = max(step_size * sa_cooling_rate, 1e-8)
178
+
179
+ return best_centers
180
+ >>>>>>> REPLACE
181
+ </DIFF>