JustinTX commited on
Commit
c14cbfc
·
verified ·
1 Parent(s): ca7518b

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/results_full_gen200_period20_20260207_182944/best/__pycache__/main.cpython-313.pyc +0 -0
  2. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/edit.diff +302 -0
  3. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/main.py +230 -0
  4. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/original.py +190 -0
  5. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/results/correct.json +4 -0
  6. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/results/metrics.json +78 -0
  7. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/rewrite.txt +283 -0
  8. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/search_replace.txt +349 -0
  9. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/eval_agent_memory/EVAL_AGENTS.md +233 -0
  10. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/eval_agent_memory/auxiliary_metrics.py +477 -0
  11. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/eval_agent_memory/service_state.json +608 -0
  12. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_0/main.py +94 -0
  13. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_1/edit.diff +201 -0
  14. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_1/main.py +153 -0
  15. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_1/original.py +94 -0
  16. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_1/rewrite.txt +144 -0
  17. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/__pycache__/main.cpython-313.pyc +0 -0
  18. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/edit.diff +184 -0
  19. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/main.py +176 -0
  20. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/original.py +174 -0
  21. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/results/correct.json +4 -0
  22. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/results/metrics.json +25 -0
  23. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/search_replace.txt +25 -0
  24. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_100/edit.diff +184 -0
  25. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_100/main.py +174 -0
  26. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_100/original.py +170 -0
  27. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_100/search_replace.txt +55 -0
  28. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_101/edit.diff +241 -0
  29. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_101/main.py +164 -0
  30. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_101/original.py +166 -0
  31. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_101/rewrite.txt +155 -0
  32. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/__pycache__/main.cpython-313.pyc +0 -0
  33. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/edit.diff +181 -0
  34. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/main.py +168 -0
  35. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/original.py +161 -0
  36. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/results/correct.json +4 -0
  37. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/results/metrics.json +84 -0
  38. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/search_replace.txt +48 -0
  39. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_103/edit.diff +207 -0
  40. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_103/main.py +196 -0
  41. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_103/original.py +189 -0
  42. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_103/search_replace.txt +46 -0
  43. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/__pycache__/main.cpython-313.pyc +0 -0
  44. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/edit.diff +200 -0
  45. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/main.py +180 -0
  46. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/original.py +174 -0
  47. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/results/correct.json +4 -0
  48. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/results/metrics.json +82 -0
  49. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/search_replace.txt +88 -0
  50. examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_106/edit.diff +231 -0
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/__pycache__/main.cpython-313.pyc ADDED
Binary file (12.5 kB). View file
 
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/edit.diff ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,190 +1,230 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+ from scipy.optimize import linprog
7
+ from dataclasses import dataclass
8
+ import math
9
+ +import random
10
+
11
+ @dataclass
12
+ class PackingConfig:
13
+ """
14
+ A unified configuration object holding all tunable parameters for the circle packing problem.
15
+ - This dataclass consolidates the most effective parameters from previous successful attempts,
16
+ - including non-uniform grids, central pair offsets, and anisotropic scaling.
17
+ + This dataclass consolidates parameters for initial placement and adds parameters for
18
+ + Simulated Annealing (SA) to refine the packing.
19
+ """
20
+ n_circles: int = 26
21
+
22
+ - # Grid parameters allowing non-uniform spacing.
23
+ - # The default is the robust 5x5 grid that has performed well.
24
+ + # Grid parameters for the initial 24 circles.
25
+ grid_x_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
26
+ grid_y_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
27
+
28
+ - # Central circle parameters. These are tuned based on the best-performing prior program (score 2.52).
29
+ - # Distance between the two central circle centers.
30
+ + # Central circle parameters for initial placement, tuned from high-scoring runs.
31
+ central_separation_distance: float = 0.125
32
+ - # The angle of the line connecting the two central circles.
33
+ central_pair_orientation_angle_deg: float = 44.5
34
+ - # The distance to shift the entire central pair's midpoint from (0.5, 0.5).
35
+ - central_midpoint_offset_dist: float = 0.0015
36
+ - # The angle of the midpoint shift. 180 degrees corresponds to a negative x-offset.
37
+ - central_midpoint_offset_angle_deg: float = 180.0
38
+ - # Anisotropic scaling to create an elliptical void for the central pair.
39
+ + # Asymmetric offset for the central pair's midpoint.
40
+ + central_midpoint_offset_dist: float = 0.0018 # From x=-0.0015, y=0.0010
41
+ + central_midpoint_offset_angle_deg: float = 146.3 # From x=-0.0015, y=0.0010
42
+ + # Anisotropic scaling for the central pair's void.
43
+ central_x_offset_scale: float = 1.01
44
+ central_y_offset_scale: float = 0.99
45
+
46
+ - # Global rotation for the entire packing arrangement.
47
+ - global_rotation_angle_deg: float = 0.0
48
+ + # Global rotation for the initial packing arrangement.
49
+ + global_rotation_angle_deg: float = 0.1
50
+ +
51
+ + # Simulated Annealing (SA) parameters
52
+ + initial_temperature: float = 0.005
53
+ + cooling_rate: float = 0.9999
54
+ + max_iterations: int = 25000
55
+ + initial_perturb_scale: float = 0.015
56
+ + perturb_temp_power: float = 1.0
57
+ + circles_to_perturb: int = 2
58
+ + global_nudge_probability: float = 0.01
59
+ + global_nudge_amount: float = 0.005
60
+ + global_nudge_temp_power: float = 0.5
61
+
62
+ clip_epsilon: float = 1e-8
63
+
64
+
65
+ class CirclePackingSolver:
66
+ """
67
+ - An encapsulated solver for the circle packing problem.
68
+ - This class takes a configuration object and orchestrates the generation of
69
+ - circle centers and the computation of their optimal radii. This new structure
70
+ - centralizes all logic into a single, reusable class.
71
+ + An encapsulated solver for the circle packing problem, now enhanced with
72
+ + a Simulated Annealing (SA) optimization loop. It starts with a strong
73
+ + initial configuration and iteratively refines it to find a better packing.
74
+ """
75
+
76
+ def __init__(self, config: PackingConfig):
77
+ """Initializes the solver with a given packing configuration."""
78
+ self.config = config
79
+ - self.centers = None
80
+ - self.radii = None
81
+ -
82
+ - def _generate_grid_centers(self) -> np.ndarray:
83
+ - """Generates centers for 24 circles arranged in a grid, skipping the central point."""
84
+ + self.current_temperature = config.initial_temperature
85
+ +
86
+ + def _generate_initial_centers(self) -> np.ndarray:
87
+ + """Generates the initial set of circle centers based on the configuration."""
88
+ + # 1. Generate grid centers
89
+ grid_centers = []
90
+ num_divs = len(self.config.grid_x_coords)
91
+ for i in range(num_divs):
92
+ for j in range(num_divs):
93
+ if i == num_divs // 2 and j == num_divs // 2:
94
+ continue
95
+ grid_centers.append([self.config.grid_x_coords[i], self.config.grid_y_coords[j]])
96
+ - return np.array(grid_centers)
97
+ -
98
+ - def _generate_central_centers(self) -> np.ndarray:
99
+ - """
100
+ - Generates centers for the 2 central circles using a flexible parameterization
101
+ - that includes midpoint offset, orientation, and anisotropic scaling.
102
+ - """
103
+ - # Calculate the central pair's midpoint with an offset from (0.5, 0.5).
104
+ +
105
+ + # 2. Generate central centers
106
+ offset_rad = math.radians(self.config.central_midpoint_offset_angle_deg)
107
+ mid_x = 0.5 + self.config.central_midpoint_offset_dist * math.cos(offset_rad)
108
+ mid_y = 0.5 + self.config.central_midpoint_offset_dist * math.sin(offset_rad)
109
+
110
+ - # Calculate the displacement vectors for the two circles from their midpoint.
111
+ R_prime = self.config.central_separation_distance / 2.0
112
+ orientation_rad = math.radians(self.config.central_pair_orientation_angle_deg)
113
+
114
+ - # Raw displacement based on orientation angle.
115
+ dx_raw = R_prime * math.cos(orientation_rad)
116
+ dy_raw = R_prime * math.sin(orientation_rad)
117
+
118
+ - # Apply anisotropic scaling.
119
+ dx = dx_raw * self.config.central_x_offset_scale
120
+ dy = dy_raw * self.config.central_y_offset_scale
121
+
122
+ - return np.array([
123
+ + central_centers = np.array([
124
+ [mid_x - dx, mid_y - dy],
125
+ [mid_x + dx, mid_y + dy]
126
+ ])
127
+
128
+ - def _apply_global_rotation(self, centers: np.ndarray) -> np.ndarray:
129
+ - """Applies a global rotation to all circle centers around (0.5, 0.5)."""
130
+ - if abs(self.config.global_rotation_angle_deg) < 1e-6:
131
+ - return centers
132
+ -
133
+ - angle_rad = math.radians(self.config.global_rotation_angle_deg)
134
+ - cos_theta, sin_theta = math.cos(angle_rad), math.sin(angle_rad)
135
+ - rotation_matrix = np.array([[cos_theta, -sin_theta], [sin_theta, cos_theta]])
136
+ -
137
+ - # Translate to origin, rotate, then translate back.
138
+ - return (rotation_matrix @ (centers - 0.5).T).T + 0.5
139
+ + all_centers = np.vstack((grid_centers, central_centers))
140
+ +
141
+ + # 3. Apply global rotation
142
+ + if abs(self.config.global_rotation_angle_deg) > 1e-6:
143
+ + angle_rad = math.radians(self.config.global_rotation_angle_deg)
144
+ + cos_theta, sin_theta = math.cos(angle_rad), math.sin(angle_rad)
145
+ + rotation_matrix = np.array([[cos_theta, -sin_theta], [sin_theta, cos_theta]])
146
+ + all_centers = (rotation_matrix @ (all_centers - 0.5).T).T + 0.5
147
+ +
148
+ + return np.clip(all_centers, self.config.clip_epsilon, 1 - self.config.clip_epsilon)
149
+ +
150
+ + def _perturb_centers(self, centers: np.ndarray) -> np.ndarray:
151
+ + """Applies local perturbations to a subset of circle centers."""
152
+ + new_centers = np.copy(centers)
153
+ + perturb_amount = self.config.initial_perturb_scale * (self.current_temperature / self.config.initial_temperature)**self.config.perturb_temp_power
154
+ +
155
+ + for _ in range(self.config.circles_to_perturb):
156
+ + circle_idx = random.randint(0, self.config.n_circles - 1)
157
+ +
158
+ + dx = random.uniform(-perturb_amount, perturb_amount)
159
+ + dy = random.uniform(-perturb_amount, perturb_amount)
160
+ +
161
+ + new_centers[circle_idx, 0] += dx
162
+ + new_centers[circle_idx, 1] += dy
163
+ +
164
+ + new_centers[circle_idx, :] = np.clip(new_centers[circle_idx, :], self.config.clip_epsilon, 1 - self.config.clip_epsilon)
165
+ +
166
+ + return new_centers
167
+ +
168
+ + def _apply_global_nudge(self, centers: np.ndarray) -> np.ndarray:
169
+ + """Applies a small global offset to all circles with a certain probability."""
170
+ + if random.random() < self.config.global_nudge_probability:
171
+ + nudge_scale = self.config.global_nudge_amount * (self.current_temperature / self.config.initial_temperature)**self.config.global_nudge_temp_power
172
+ + global_dx = random.uniform(-nudge_scale, nudge_scale)
173
+ + global_dy = random.uniform(-nudge_scale, nudge_scale)
174
+ + centers[:, 0] += global_dx
175
+ + centers[:, 1] += global_dy
176
+ + centers = np.clip(centers, self.config.clip_epsilon, 1 - self.config.clip_epsilon)
177
+ + return centers
178
+
179
+ def solve(self) -> tuple[np.ndarray, np.ndarray]:
180
+ """
181
+ - Executes the full packing and solving pipeline.
182
+ - 1. Generates centers for all circles.
183
+ - 2. Applies global transformations.
184
+ - 3. Computes the maximum possible radii using linear programming.
185
+ - """
186
+ - grid_centers = self._generate_grid_centers()
187
+ - central_centers = self._generate_central_centers()
188
+ -
189
+ - all_centers = np.vstack((grid_centers, central_centers))
190
+ - all_centers = self._apply_global_rotation(all_centers)
191
+ -
192
+ - # Clip to ensure centers are strictly inside the unit square.
193
+ - self.centers = np.clip(all_centers, self.config.clip_epsilon, 1 - self.config.clip_epsilon)
194
+ -
195
+ - self.radii = self._compute_max_radii(self.centers)
196
+ -
197
+ - return self.centers, self.radii
198
+ + Executes the full SA optimization pipeline.
199
+ + 1. Generates a strong initial configuration.
200
+ + 2. Iteratively perturbs centers and accepts new states based on SA logic.
201
+ + 3. Returns the best configuration found.
202
+ + """
203
+ + current_centers = self._generate_initial_centers()
204
+ + current_sum_radii = np.sum(self._compute_max_radii(current_centers))
205
+ +
206
+ + best_centers = np.copy(current_centers)
207
+ + best_sum_radii = current_sum_radii
208
+ +
209
+ + for _ in range(self.config.max_iterations):
210
+ + candidate_centers = self._perturb_centers(current_centers)
211
+ + candidate_centers = self._apply_global_nudge(candidate_centers)
212
+ +
213
+ + candidate_sum_radii = np.sum(self._compute_max_radii(candidate_centers))
214
+ +
215
+ + delta_E = candidate_sum_radii - current_sum_radii
216
+ +
217
+ + if delta_E > 0 or (self.current_temperature > 0 and random.random() < math.exp(delta_E / self.current_temperature)):
218
+ + current_centers = candidate_centers
219
+ + current_sum_radii = candidate_sum_radii
220
+ + if current_sum_radii > best_sum_radii:
221
+ + best_centers = np.copy(current_centers)
222
+ + best_sum_radii = current_sum_radii
223
+ +
224
+ + self.current_temperature *= self.config.cooling_rate
225
+ +
226
+ + final_radii = self._compute_max_radii(best_centers)
227
+ + return best_centers, final_radii
228
+
229
+ @staticmethod
230
+ def _compute_max_radii(centers: np.ndarray) -> np.ndarray:
231
+ """
232
+ Computes maximum radii for given centers by solving a Linear Programming problem.
233
+ This is a static method as its logic is independent of any specific solver instance.
234
+ """
235
+ n = centers.shape[0]
236
+ c = -np.ones(n) # Objective: maximize sum(r_i) -> minimize sum(-r_i)
237
+
238
+ num_boundary_constraints = 4 * n
239
+ num_pairwise_constraints = n * (n - 1) // 2
240
+ num_constraints = num_boundary_constraints + num_pairwise_constraints
241
+
242
+ A_ub = np.zeros((num_constraints, n))
243
+ b_ub = np.zeros(num_constraints)
244
+
245
+ row_idx = 0
246
+ # Boundary constraints: r_i <= dist_to_wall
247
+ for i in range(n):
248
+ x, y = centers[i]
249
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = x; row_idx += 1
250
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = 1 - x; row_idx += 1
251
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = y; row_idx += 1
252
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = 1 - y; row_idx += 1
253
+
254
+ # Pairwise non-overlapping constraints: r_i + r_j <= dist(c_i, c_j)
255
+ for i in range(n):
256
+ for j in range(i + 1, n):
257
+ dist = np.linalg.norm(centers[i] - centers[j])
258
+ A_ub[row_idx, i] = 1
259
+ A_ub[row_idx, j] = 1
260
+ b_ub[row_idx] = dist
261
+ row_idx += 1
262
+
263
+ bounds = (0, None) # Radii must be non-negative.
264
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
265
+
266
+ return res.x if res.success else np.zeros(n)
267
+
268
+ def construct_packing():
269
+ """
270
+ Main entry point for constructing the circle packing.
271
+ - This function now uses the unified CirclePackingSolver class, demonstrating the
272
+ - new, cleaner architecture. The configuration is instantiated with parameters
273
+ - derived from the best-performing historical programs to aim for a new high score.
274
+ - """
275
+ - # This configuration is a refined combination of the best parameters observed
276
+ - # in previous high-scoring runs (scores > 2.51).
277
+ + This function uses the CirclePackingSolver, which implements a Simulated
278
+ + Annealing (SA) algorithm. It starts with a strong initial configuration and
279
+ + then uses SA to search for improvements.
280
+ + """
281
+ + # The configuration is instantiated with default parameters that include
282
+ + # both the initial placement logic and the SA optimization parameters.
283
+ + # These defaults are tuned from previous high-scoring runs.
284
+ config = PackingConfig()
285
+
286
+ # Create a solver instance with the specified configuration.
287
+ solver = CirclePackingSolver(config)
288
+
289
+ # Run the solver to get the final centers and radii.
290
+ centers, radii = solver.solve()
291
+
292
+ return centers, radii
293
+ # EVOLVE-BLOCK-END
294
+
295
+
296
+ # This part remains fixed (not evolved)
297
+ def run_packing():
298
+ """Run the circle packing constructor for n=26"""
299
+ centers, radii = construct_packing()
300
+ # Calculate the sum of radii
301
+ sum_radii = np.sum(radii)
302
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/main.py ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from scipy.optimize import linprog
4
+ from dataclasses import dataclass
5
+ import math
6
+ import random
7
+
8
+ @dataclass
9
+ class PackingConfig:
10
+ """
11
+ A unified configuration object holding all tunable parameters for the circle packing problem.
12
+ This dataclass consolidates parameters for initial placement and adds parameters for
13
+ Simulated Annealing (SA) to refine the packing.
14
+ """
15
+ n_circles: int = 26
16
+
17
+ # Grid parameters for the initial 24 circles.
18
+ grid_x_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
19
+ grid_y_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
20
+
21
+ # Central circle parameters for initial placement, tuned from high-scoring runs.
22
+ central_separation_distance: float = 0.125
23
+ central_pair_orientation_angle_deg: float = 44.5
24
+ # Asymmetric offset for the central pair's midpoint.
25
+ central_midpoint_offset_dist: float = 0.0018 # From x=-0.0015, y=0.0010
26
+ central_midpoint_offset_angle_deg: float = 146.3 # From x=-0.0015, y=0.0010
27
+ # Anisotropic scaling for the central pair's void.
28
+ central_x_offset_scale: float = 1.01
29
+ central_y_offset_scale: float = 0.99
30
+
31
+ # Global rotation for the initial packing arrangement.
32
+ global_rotation_angle_deg: float = 0.1
33
+
34
+ # Simulated Annealing (SA) parameters
35
+ initial_temperature: float = 0.005
36
+ cooling_rate: float = 0.9999
37
+ max_iterations: int = 25000
38
+ initial_perturb_scale: float = 0.015
39
+ perturb_temp_power: float = 1.0
40
+ circles_to_perturb: int = 2
41
+ global_nudge_probability: float = 0.01
42
+ global_nudge_amount: float = 0.005
43
+ global_nudge_temp_power: float = 0.5
44
+
45
+ clip_epsilon: float = 1e-8
46
+
47
+
48
+ class CirclePackingSolver:
49
+ """
50
+ An encapsulated solver for the circle packing problem, now enhanced with
51
+ a Simulated Annealing (SA) optimization loop. It starts with a strong
52
+ initial configuration and iteratively refines it to find a better packing.
53
+ """
54
+
55
+ def __init__(self, config: PackingConfig):
56
+ """Initializes the solver with a given packing configuration."""
57
+ self.config = config
58
+ self.current_temperature = config.initial_temperature
59
+
60
+ def _generate_initial_centers(self) -> np.ndarray:
61
+ """Generates the initial set of circle centers based on the configuration."""
62
+ # 1. Generate grid centers
63
+ grid_centers = []
64
+ num_divs = len(self.config.grid_x_coords)
65
+ for i in range(num_divs):
66
+ for j in range(num_divs):
67
+ if i == num_divs // 2 and j == num_divs // 2:
68
+ continue
69
+ grid_centers.append([self.config.grid_x_coords[i], self.config.grid_y_coords[j]])
70
+
71
+ # 2. Generate central centers
72
+ offset_rad = math.radians(self.config.central_midpoint_offset_angle_deg)
73
+ mid_x = 0.5 + self.config.central_midpoint_offset_dist * math.cos(offset_rad)
74
+ mid_y = 0.5 + self.config.central_midpoint_offset_dist * math.sin(offset_rad)
75
+
76
+ R_prime = self.config.central_separation_distance / 2.0
77
+ orientation_rad = math.radians(self.config.central_pair_orientation_angle_deg)
78
+
79
+ dx_raw = R_prime * math.cos(orientation_rad)
80
+ dy_raw = R_prime * math.sin(orientation_rad)
81
+
82
+ dx = dx_raw * self.config.central_x_offset_scale
83
+ dy = dy_raw * self.config.central_y_offset_scale
84
+
85
+ central_centers = np.array([
86
+ [mid_x - dx, mid_y - dy],
87
+ [mid_x + dx, mid_y + dy]
88
+ ])
89
+
90
+ all_centers = np.vstack((grid_centers, central_centers))
91
+
92
+ # 3. Apply global rotation
93
+ if abs(self.config.global_rotation_angle_deg) > 1e-6:
94
+ angle_rad = math.radians(self.config.global_rotation_angle_deg)
95
+ cos_theta, sin_theta = math.cos(angle_rad), math.sin(angle_rad)
96
+ rotation_matrix = np.array([[cos_theta, -sin_theta], [sin_theta, cos_theta]])
97
+ all_centers = (rotation_matrix @ (all_centers - 0.5).T).T + 0.5
98
+
99
+ return np.clip(all_centers, self.config.clip_epsilon, 1 - self.config.clip_epsilon)
100
+
101
+ def _perturb_centers(self, centers: np.ndarray) -> np.ndarray:
102
+ """Applies local perturbations to a subset of circle centers."""
103
+ new_centers = np.copy(centers)
104
+ perturb_amount = self.config.initial_perturb_scale * (self.current_temperature / self.config.initial_temperature)**self.config.perturb_temp_power
105
+
106
+ for _ in range(self.config.circles_to_perturb):
107
+ circle_idx = random.randint(0, self.config.n_circles - 1)
108
+
109
+ dx = random.uniform(-perturb_amount, perturb_amount)
110
+ dy = random.uniform(-perturb_amount, perturb_amount)
111
+
112
+ new_centers[circle_idx, 0] += dx
113
+ new_centers[circle_idx, 1] += dy
114
+
115
+ new_centers[circle_idx, :] = np.clip(new_centers[circle_idx, :], self.config.clip_epsilon, 1 - self.config.clip_epsilon)
116
+
117
+ return new_centers
118
+
119
+ def _apply_global_nudge(self, centers: np.ndarray) -> np.ndarray:
120
+ """Applies a small global offset to all circles with a certain probability."""
121
+ if random.random() < self.config.global_nudge_probability:
122
+ nudge_scale = self.config.global_nudge_amount * (self.current_temperature / self.config.initial_temperature)**self.config.global_nudge_temp_power
123
+ global_dx = random.uniform(-nudge_scale, nudge_scale)
124
+ global_dy = random.uniform(-nudge_scale, nudge_scale)
125
+ centers[:, 0] += global_dx
126
+ centers[:, 1] += global_dy
127
+ centers = np.clip(centers, self.config.clip_epsilon, 1 - self.config.clip_epsilon)
128
+ return centers
129
+
130
+ def solve(self) -> tuple[np.ndarray, np.ndarray]:
131
+ """
132
+ Executes the full SA optimization pipeline.
133
+ 1. Generates a strong initial configuration.
134
+ 2. Iteratively perturbs centers and accepts new states based on SA logic.
135
+ 3. Returns the best configuration found.
136
+ """
137
+ current_centers = self._generate_initial_centers()
138
+ current_sum_radii = np.sum(self._compute_max_radii(current_centers))
139
+
140
+ best_centers = np.copy(current_centers)
141
+ best_sum_radii = current_sum_radii
142
+
143
+ for _ in range(self.config.max_iterations):
144
+ candidate_centers = self._perturb_centers(current_centers)
145
+ candidate_centers = self._apply_global_nudge(candidate_centers)
146
+
147
+ candidate_sum_radii = np.sum(self._compute_max_radii(candidate_centers))
148
+
149
+ delta_E = candidate_sum_radii - current_sum_radii
150
+
151
+ if delta_E > 0 or (self.current_temperature > 0 and random.random() < math.exp(delta_E / self.current_temperature)):
152
+ current_centers = candidate_centers
153
+ current_sum_radii = candidate_sum_radii
154
+ if current_sum_radii > best_sum_radii:
155
+ best_centers = np.copy(current_centers)
156
+ best_sum_radii = current_sum_radii
157
+
158
+ self.current_temperature *= self.config.cooling_rate
159
+
160
+ final_radii = self._compute_max_radii(best_centers)
161
+ return best_centers, final_radii
162
+
163
+ @staticmethod
164
+ def _compute_max_radii(centers: np.ndarray) -> np.ndarray:
165
+ """
166
+ Computes maximum radii for given centers by solving a Linear Programming problem.
167
+ This is a static method as its logic is independent of any specific solver instance.
168
+ """
169
+ n = centers.shape[0]
170
+ c = -np.ones(n) # Objective: maximize sum(r_i) -> minimize sum(-r_i)
171
+
172
+ num_boundary_constraints = 4 * n
173
+ num_pairwise_constraints = n * (n - 1) // 2
174
+ num_constraints = num_boundary_constraints + num_pairwise_constraints
175
+
176
+ A_ub = np.zeros((num_constraints, n))
177
+ b_ub = np.zeros(num_constraints)
178
+
179
+ row_idx = 0
180
+ # Boundary constraints: r_i <= dist_to_wall
181
+ for i in range(n):
182
+ x, y = centers[i]
183
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = x; row_idx += 1
184
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = 1 - x; row_idx += 1
185
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = y; row_idx += 1
186
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = 1 - y; row_idx += 1
187
+
188
+ # Pairwise non-overlapping constraints: r_i + r_j <= dist(c_i, c_j)
189
+ for i in range(n):
190
+ for j in range(i + 1, n):
191
+ dist = np.linalg.norm(centers[i] - centers[j])
192
+ A_ub[row_idx, i] = 1
193
+ A_ub[row_idx, j] = 1
194
+ b_ub[row_idx] = dist
195
+ row_idx += 1
196
+
197
+ bounds = (0, None) # Radii must be non-negative.
198
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
199
+
200
+ return res.x if res.success else np.zeros(n)
201
+
202
+ def construct_packing():
203
+ """
204
+ Main entry point for constructing the circle packing.
205
+ This function uses the CirclePackingSolver, which implements a Simulated
206
+ Annealing (SA) algorithm. It starts with a strong initial configuration and
207
+ then uses SA to search for improvements.
208
+ """
209
+ # The configuration is instantiated with default parameters that include
210
+ # both the initial placement logic and the SA optimization parameters.
211
+ # These defaults are tuned from previous high-scoring runs.
212
+ config = PackingConfig()
213
+
214
+ # Create a solver instance with the specified configuration.
215
+ solver = CirclePackingSolver(config)
216
+
217
+ # Run the solver to get the final centers and radii.
218
+ centers, radii = solver.solve()
219
+
220
+ return centers, radii
221
+ # EVOLVE-BLOCK-END
222
+
223
+
224
+ # This part remains fixed (not evolved)
225
+ def run_packing():
226
+ """Run the circle packing constructor for n=26"""
227
+ centers, radii = construct_packing()
228
+ # Calculate the sum of radii
229
+ sum_radii = np.sum(radii)
230
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/original.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from scipy.optimize import linprog
4
+ from dataclasses import dataclass
5
+ import math
6
+
7
+ @dataclass
8
+ class PackingConfig:
9
+ """
10
+ A unified configuration object holding all tunable parameters for the circle packing problem.
11
+ This dataclass consolidates the most effective parameters from previous successful attempts,
12
+ including non-uniform grids, central pair offsets, and anisotropic scaling.
13
+ """
14
+ n_circles: int = 26
15
+
16
+ # Grid parameters allowing non-uniform spacing.
17
+ # The default is the robust 5x5 grid that has performed well.
18
+ grid_x_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
19
+ grid_y_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
20
+
21
+ # Central circle parameters. These are tuned based on the best-performing prior program (score 2.52).
22
+ # Distance between the two central circle centers.
23
+ central_separation_distance: float = 0.125
24
+ # The angle of the line connecting the two central circles.
25
+ central_pair_orientation_angle_deg: float = 44.5
26
+ # The distance to shift the entire central pair's midpoint from (0.5, 0.5).
27
+ central_midpoint_offset_dist: float = 0.0015
28
+ # The angle of the midpoint shift. 180 degrees corresponds to a negative x-offset.
29
+ central_midpoint_offset_angle_deg: float = 180.0
30
+ # Anisotropic scaling to create an elliptical void for the central pair.
31
+ central_x_offset_scale: float = 1.01
32
+ central_y_offset_scale: float = 0.99
33
+
34
+ # Global rotation for the entire packing arrangement.
35
+ global_rotation_angle_deg: float = 0.0
36
+
37
+ clip_epsilon: float = 1e-8
38
+
39
+
40
+ class CirclePackingSolver:
41
+ """
42
+ An encapsulated solver for the circle packing problem.
43
+ This class takes a configuration object and orchestrates the generation of
44
+ circle centers and the computation of their optimal radii. This new structure
45
+ centralizes all logic into a single, reusable class.
46
+ """
47
+
48
+ def __init__(self, config: PackingConfig):
49
+ """Initializes the solver with a given packing configuration."""
50
+ self.config = config
51
+ self.centers = None
52
+ self.radii = None
53
+
54
+ def _generate_grid_centers(self) -> np.ndarray:
55
+ """Generates centers for 24 circles arranged in a grid, skipping the central point."""
56
+ grid_centers = []
57
+ num_divs = len(self.config.grid_x_coords)
58
+ for i in range(num_divs):
59
+ for j in range(num_divs):
60
+ if i == num_divs // 2 and j == num_divs // 2:
61
+ continue
62
+ grid_centers.append([self.config.grid_x_coords[i], self.config.grid_y_coords[j]])
63
+ return np.array(grid_centers)
64
+
65
+ def _generate_central_centers(self) -> np.ndarray:
66
+ """
67
+ Generates centers for the 2 central circles using a flexible parameterization
68
+ that includes midpoint offset, orientation, and anisotropic scaling.
69
+ """
70
+ # Calculate the central pair's midpoint with an offset from (0.5, 0.5).
71
+ offset_rad = math.radians(self.config.central_midpoint_offset_angle_deg)
72
+ mid_x = 0.5 + self.config.central_midpoint_offset_dist * math.cos(offset_rad)
73
+ mid_y = 0.5 + self.config.central_midpoint_offset_dist * math.sin(offset_rad)
74
+
75
+ # Calculate the displacement vectors for the two circles from their midpoint.
76
+ R_prime = self.config.central_separation_distance / 2.0
77
+ orientation_rad = math.radians(self.config.central_pair_orientation_angle_deg)
78
+
79
+ # Raw displacement based on orientation angle.
80
+ dx_raw = R_prime * math.cos(orientation_rad)
81
+ dy_raw = R_prime * math.sin(orientation_rad)
82
+
83
+ # Apply anisotropic scaling.
84
+ dx = dx_raw * self.config.central_x_offset_scale
85
+ dy = dy_raw * self.config.central_y_offset_scale
86
+
87
+ return np.array([
88
+ [mid_x - dx, mid_y - dy],
89
+ [mid_x + dx, mid_y + dy]
90
+ ])
91
+
92
+ def _apply_global_rotation(self, centers: np.ndarray) -> np.ndarray:
93
+ """Applies a global rotation to all circle centers around (0.5, 0.5)."""
94
+ if abs(self.config.global_rotation_angle_deg) < 1e-6:
95
+ return centers
96
+
97
+ angle_rad = math.radians(self.config.global_rotation_angle_deg)
98
+ cos_theta, sin_theta = math.cos(angle_rad), math.sin(angle_rad)
99
+ rotation_matrix = np.array([[cos_theta, -sin_theta], [sin_theta, cos_theta]])
100
+
101
+ # Translate to origin, rotate, then translate back.
102
+ return (rotation_matrix @ (centers - 0.5).T).T + 0.5
103
+
104
+ def solve(self) -> tuple[np.ndarray, np.ndarray]:
105
+ """
106
+ Executes the full packing and solving pipeline.
107
+ 1. Generates centers for all circles.
108
+ 2. Applies global transformations.
109
+ 3. Computes the maximum possible radii using linear programming.
110
+ """
111
+ grid_centers = self._generate_grid_centers()
112
+ central_centers = self._generate_central_centers()
113
+
114
+ all_centers = np.vstack((grid_centers, central_centers))
115
+ all_centers = self._apply_global_rotation(all_centers)
116
+
117
+ # Clip to ensure centers are strictly inside the unit square.
118
+ self.centers = np.clip(all_centers, self.config.clip_epsilon, 1 - self.config.clip_epsilon)
119
+
120
+ self.radii = self._compute_max_radii(self.centers)
121
+
122
+ return self.centers, self.radii
123
+
124
+ @staticmethod
125
+ def _compute_max_radii(centers: np.ndarray) -> np.ndarray:
126
+ """
127
+ Computes maximum radii for given centers by solving a Linear Programming problem.
128
+ This is a static method as its logic is independent of any specific solver instance.
129
+ """
130
+ n = centers.shape[0]
131
+ c = -np.ones(n) # Objective: maximize sum(r_i) -> minimize sum(-r_i)
132
+
133
+ num_boundary_constraints = 4 * n
134
+ num_pairwise_constraints = n * (n - 1) // 2
135
+ num_constraints = num_boundary_constraints + num_pairwise_constraints
136
+
137
+ A_ub = np.zeros((num_constraints, n))
138
+ b_ub = np.zeros(num_constraints)
139
+
140
+ row_idx = 0
141
+ # Boundary constraints: r_i <= dist_to_wall
142
+ for i in range(n):
143
+ x, y = centers[i]
144
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = x; row_idx += 1
145
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = 1 - x; row_idx += 1
146
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = y; row_idx += 1
147
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = 1 - y; row_idx += 1
148
+
149
+ # Pairwise non-overlapping constraints: r_i + r_j <= dist(c_i, c_j)
150
+ for i in range(n):
151
+ for j in range(i + 1, n):
152
+ dist = np.linalg.norm(centers[i] - centers[j])
153
+ A_ub[row_idx, i] = 1
154
+ A_ub[row_idx, j] = 1
155
+ b_ub[row_idx] = dist
156
+ row_idx += 1
157
+
158
+ bounds = (0, None) # Radii must be non-negative.
159
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
160
+
161
+ return res.x if res.success else np.zeros(n)
162
+
163
+ def construct_packing():
164
+ """
165
+ Main entry point for constructing the circle packing.
166
+ This function now uses the unified CirclePackingSolver class, demonstrating the
167
+ new, cleaner architecture. The configuration is instantiated with parameters
168
+ derived from the best-performing historical programs to aim for a new high score.
169
+ """
170
+ # This configuration is a refined combination of the best parameters observed
171
+ # in previous high-scoring runs (scores > 2.51).
172
+ config = PackingConfig()
173
+
174
+ # Create a solver instance with the specified configuration.
175
+ solver = CirclePackingSolver(config)
176
+
177
+ # Run the solver to get the final centers and radii.
178
+ centers, radii = solver.solve()
179
+
180
+ return centers, radii
181
+ # EVOLVE-BLOCK-END
182
+
183
+
184
+ # This part remains fixed (not evolved)
185
+ def run_packing():
186
+ """Run the circle packing constructor for n=26"""
187
+ centers, radii = construct_packing()
188
+ # Calculate the sum of radii
189
+ sum_radii = np.sum(radii)
190
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/results/correct.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "correct": true,
3
+ "error": null
4
+ }
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/results/metrics.json ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "combined_score": 2.593165588404451,
3
+ "correct": true,
4
+ "primary": {
5
+ "combined_score": 2.593165588404451,
6
+ "public": {
7
+ "centers_str": " centers[0] = (0.0679, 0.0686)\n centers[1] = (0.0934, 0.2279)\n centers[2] = (0.1147, 0.4305)\n centers[3] = (0.1414, 0.6791)\n centers[4] = (0.0933, 0.9057)\n centers[5] = (0.2277, 0.0932)\n centers[6] = (0.2864, 0.2905)\n centers[7] = (0.3211, 0.5081)\n centers[8] = (0.3826, 0.7171)\n centers[9] = (0.2873, 0.8997)\n centers[10] = (0.4353, 0.1156)\n centers[11] = (0.5345, 0.3583)\n centers[12] = (0.5863, 0.7286)\n centers[13] = (0.4887, 0.8982)\n centers[14] = (0.6521, 0.1018)\n centers[15] = (0.7381, 0.2592)\n centers[16] = (0.7523, 0.4091)\n centers[17] = (0.7642, 0.7091)\n centers[18] = (0.6978, 0.8933)\n centers[19] = (0.8817, 0.1185)\n centers[20] = (0.9023, 0.3319)\n centers[21] = (0.8833, 0.5459)\n centers[22] = (0.9219, 0.7357)\n centers[23] = (0.8991, 0.9038)\n centers[24] = (0.5032, 0.5794)\n centers[25] = (0.6724, 0.5555)",
8
+ "num_circles": 26
9
+ },
10
+ "private": {
11
+ "reported_sum_of_radii": 2.593165588404451
12
+ },
13
+ "execution_time_mean": 101.22933560982347,
14
+ "execution_time_std": 0.0,
15
+ "num_valid_runs": 1,
16
+ "num_invalid_runs": 0,
17
+ "all_validation_errors": [],
18
+ "correct": true,
19
+ "validation_error": null
20
+ },
21
+ "auxiliary": {
22
+ "avg_radius": 0.09973713801555581,
23
+ "std_dev_radius": 0.01845283729104012,
24
+ "min_radius": 0.06786836138638622,
25
+ "max_radius": 0.1465279758088699,
26
+ "median_radius": 0.0978725509471671,
27
+ "num_unique_radii": 26,
28
+ "radius_coefficient_of_variation": 0.18501470623873392,
29
+ "total_area_covered": 0.8403386545769049,
30
+ "packing_density": 0.8403386545769049,
31
+ "empty_space_ratio": 0.15966134542309507,
32
+ "max_circle_overlap_magnitude": 0.0,
33
+ "num_overlapping_pairs": 0,
34
+ "max_boundary_violation_magnitude": 0.0,
35
+ "num_boundary_violations": 0,
36
+ "packing_aspect_ratio_of_centers_bbox": 1.020243012479224,
37
+ "avg_num_touching_neighbors": 1.4615384615384615,
38
+ "avg_quadrant_radii_std_dev": 0.017248158526820698,
39
+ "avg_pairwise_center_distance": 0.529041490849474,
40
+ "avg_distance_from_packing_centroid_normalized": 0.5318890800035825,
41
+ "num_circles_touching_edge": 7,
42
+ "num_circles_touching_corner": 0,
43
+ "num_circles_touching_boundary": 7,
44
+ "avg_min_distance_to_boundary": 0.07903500981585891
45
+ },
46
+ "auxiliary_descriptions": {
47
+ "avg_radius": "The average radius of the 26 circles. This provides a central tendency for circle sizes, helping to understand if the solution favors larger, smaller, or uniformly sized circles.",
48
+ "std_dev_radius": "This metric is critical for identifying potential local optima. If `std_dev_radius` is very low and `sum(radii)` is stagnating, it might suggest the algorithm is stuck with uniform-sized circles. Encouraging greater `std_dev_radius` could lead to exploring more diverse solutions (e.g., using a few large circles and many small ones) that might break current plateaus. Conversely, if `std_dev_radius` is high, it shows exploration of diverse sizes is still active.",
49
+ "min_radius": "The smallest radius among the 26 circles. Useful for identifying if very small circles are being used to fill tiny gaps.",
50
+ "max_radius": "The largest radius among the 26 circles. Indicates the largest circle size successfully placed, which is often a key factor in maximizing total radius sum.",
51
+ "median_radius": "The median radius of all circles. A robust measure of central tendency, less affected by outliers than the mean.",
52
+ "num_unique_radii": "The count of distinct radius values. A higher number suggests more variety in the chosen circle sizes.",
53
+ "total_area_covered": "By tracking this, we can see if increases in `sum(radii)` are also leading to proportional increases in actual area covered. If `sum(radii)` plateaus but `total_area_covered` continues to rise (or maintains a high level), it suggests a more efficient use of space by re-distributing radii or centers to fill gaps more effectively.",
54
+ "packing_density": "`total_area_covered` divided by the area of the unit square (which is 1). Expresses the proportion of the unit square covered by circles (0-1 range). Higher is better.",
55
+ "empty_space_ratio": "Represents the proportion of the unit square's area not covered by circles. This directly indicates the amount of \"wasted\" space. A consistently decreasing `empty_space_ratio` would indicate improving packing efficiency beyond just the sum of radii.",
56
+ "max_circle_overlap_magnitude": "The largest positive value (sum of radii - distance between centers) for any overlapping pair. _Even if the primary evaluator reports no overlap (due to internal tolerances), a small positive value here indicates a \"near miss\" or a potential instability. Should be 0 for truly valid solutions._",
57
+ "num_overlapping_pairs": "The total count of pairs of circles that overlap. _Should be 0 for valid solutions. If > 0, it highlights invalid solutions and indicates the severity of the overlap problem._",
58
+ "max_boundary_violation_magnitude": "The largest extent to which any circle protrudes beyond the unit square boundary. _Similar to overlap magnitude, it identifies near-boundary violations or slight excursions. Should be 0 for truly valid solutions._",
59
+ "num_boundary_violations": "The total count of circles that violate any boundary. _Should be 0 for valid solutions. If > 0, indicates an invalid solution._",
60
+ "packing_aspect_ratio_of_centers_bbox": "This calculates the aspect ratio of the bounding box that encloses all circle centers. It helps determine if the distribution of circles is becoming more square-like (aspect ratio closer to 1) or more elongated, which could indicate a preferred packing orientation. This is useful for detecting structural biases.",
61
+ "avg_num_touching_neighbors": "The average number of circles that each circle is touching (within a small tolerance). A higher value suggests a more interconnected and tightly packed arrangement, which is usually a characteristic of efficient packing. This can act as a proxy for the 'connectivity' of the packing.",
62
+ "avg_quadrant_radii_std_dev": "The average of the standard deviations of radii within each of the four quadrants. This helps reveal if the diversity in circle sizes is localized to certain areas or if it's generally consistent across the entire packing. High values in specific quadrants might point to localized strategies for filling space.",
63
+ "avg_pairwise_center_distance": "The average Euclidean distance between all unique pairs of circle centers. A higher value suggests a more spread-out arrangement of circles, potentially indicating a less \"clustered\" or more uniform distribution of centers. This complements nearest-neighbor distance by considering all pairs and provides a global measure of center dispersion.",
64
+ "avg_distance_from_packing_centroid_normalized": "Measures how concentrated or dispersed the circle centers are. It calculates the average distance of each circle center from the overall centroid of all circle centers, normalized by the maximum possible distance a center could be from the unit square's center (0.5, 0.5) to a corner (0,0). A lower value indicates a more centrally focused and compact packing.",
65
+ "num_circles_touching_edge": "Counts the number of circles whose edges are \"touching\" (within a small tolerance of 1e-6) any of the four boundaries of the unit square. A higher count indicates more aggressive utilization of the available space by pushing circles against the container walls.",
66
+ "num_circles_touching_corner": "Counts the number of circles whose edges are \"touching\" (within a small tolerance of 1e-6) two perpendicular boundaries (i.e., a corner) of the unit square. _A more specific measure of boundary utilization, indicating how well corners are filled._",
67
+ "avg_min_circle_to_boundary_distance": "The average of the minimum distances from each circle's edge to the closest boundary. _Smaller positive values indicate a tighter fit of circles against the edges, while larger values suggest more \"cushion\" space._",
68
+ "avg_min_distance_to_boundary": "The average of the minimum distances from each circle's edge to its closest unit square boundary. Lower values indicate tighter packing against the container walls, which is generally desirable for dense packing.",
69
+ "min_overall_distance_to_boundary": "The single smallest distance from any circle's edge to any boundary. This metric highlights if at least one circle is positioned extremely close to a boundary, indicating a strategy to maximize space.",
70
+ "radius_coefficient_of_variation": "The ratio of the standard deviation to the mean radius. A normalized measure of dispersion, useful for comparing variability across different scales of radii.",
71
+ "num_circles_touching_boundary": "Count of circles whose edge is within a small epsilon (1e-4) of any of the unit square boundaries (x=0, x=1, y=0, y=1). Indicates how many circles are actively utilizing the perimeter of the packing area.",
72
+ "avg_nearest_neighbor_distance_centers": "This metric calculates the average Euclidean distance from each circle's center to the center of its closest neighboring circle. A lower value indicates a more tightly packed or clustered arrangement of circle centers, while a higher value suggests a more dispersed or sparse distribution. This is useful for identifying if the packing strategy is creating dense clusters or leaving significant gaps between groups of circles.",
73
+ "center_quadrant_density_variance": "This metric divides the unit square into four equal quadrants (2x2 grid) and computes the variance of the number of circle centers found within each quadrant. A lower variance indicates a more uniform distribution of circle centers across the entire packing area, suggesting a balanced utilization of space. A higher variance implies that circle centers are concentrated in certain quadrants, potentially indicating inefficient or uneven packing.",
74
+ "avg_min_boundary_distance": "As solutions converge, an efficient packing should optimally utilize the boundaries. A consistently low (approaching zero) `avg_min_boundary_distance` combined with a high primary score indicates a well-optimized solution that is effectively using the edges of the unit square. If this value is still relatively high, it might suggest opportunities to push circles closer to the container's perimeter, thereby creating more space internally for larger circles or more circles."
75
+ },
76
+ "timestamp": 1770505036.2236722,
77
+ "generation": 193
78
+ }
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/rewrite.txt ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ import numpy as np
3
+ from scipy.optimize import linprog
4
+ import math
5
+ import random
6
+ from dataclasses import dataclass
7
+
8
+ # The compute_max_radii function is critical and stable, keeping it as a standalone utility.
9
+ def compute_max_radii(centers: np.ndarray) -> np.ndarray:
10
+ """
11
+ Computes the maximum possible radii for a given set of circle centers
12
+ by solving a Linear Programming (LP) problem. This maximizes the sum of radii
13
+ subject to non-overlapping and boundary constraints, ensuring optimality for fixed centers.
14
+ """
15
+ n = centers.shape[0]
16
+ c = -np.ones(n) # Objective: maximize sum(r_i) -> minimize sum(-r_i)
17
+
18
+ # Pre-calculate the number of constraints for efficient matrix allocation.
19
+ num_boundary_constraints = 4 * n # 4 walls per circle
20
+ num_pairwise_constraints = n * (n - 1) // 2 # nC2 pairs
21
+ num_constraints = num_boundary_constraints + num_pairwise_constraints
22
+
23
+ A_ub = np.zeros((num_constraints, n))
24
+ b_ub = np.zeros(num_constraints)
25
+
26
+ row_idx = 0
27
+ # 1. Add boundary constraints (r_i <= distance_to_wall).
28
+ for i in range(n):
29
+ x, y = centers[i]
30
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = x; row_idx += 1 # r_i <= x
31
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = 1 - x; row_idx += 1 # r_i <= 1 - x
32
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = y; row_idx += 1 # r_i <= y
33
+ A_ub[row_idx, i] = 1; b_ub[row_idx] = 1 - y; row_idx += 1 # r_i <= 1 - y
34
+
35
+ # 2. Add pairwise non-overlapping constraints (r_i + r_j <= dist(c_i, c_j)).
36
+ for i in range(n):
37
+ for j in range(i + 1, n):
38
+ dist = np.linalg.norm(centers[i] - centers[j])
39
+ A_ub[row_idx, i] = 1
40
+ A_ub[row_idx, j] = 1
41
+ b_ub[row_idx] = dist
42
+ row_idx += 1
43
+
44
+ # All radii must be non-negative.
45
+ bounds = (0, None)
46
+
47
+ # Solve the linear programming problem using the 'highs' solver for performance.
48
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
49
+
50
+ if res.success:
51
+ return res.x
52
+ else:
53
+ # Fallback for solver failure: return zeros and print a warning.
54
+ # print(f"Warning: LP solver failed. Status: {res.status}, Message: {res.message}") # Suppress for cleaner output
55
+ return np.zeros(n)
56
+
57
+
58
+ @dataclass
59
+ class CirclePackingConfig:
60
+ """
61
+ Configuration parameters for the Simulated Annealing circle packing process.
62
+ Encapsulates all tunable parameters for clarity, reproducibility, and easier tuning.
63
+ """
64
+ n_circles: int = 26
65
+
66
+ # Parameters for the initial 5x5 grid (24 circles)
67
+ grid_x_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
68
+ grid_y_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
69
+ grid_dim: int = 5 # Should match len(grid_x_coords) and len(grid_y_coords)
70
+
71
+ # Parameters for the 2 central circles
72
+ central_separation_distance: float = 0.125
73
+ central_pair_orientation_angle_deg: float = 44.5
74
+ central_midpoint_offset_x: float = -0.0015
75
+ central_midpoint_offset_y: float = 0.0010
76
+ central_x_offset_scale: float = 1.01
77
+ central_y_offset_scale: float = 0.99
78
+
79
+ # Global rotation applied to the *initial* configuration
80
+ initial_global_rotation_angle_deg: float = 0.1
81
+
82
+ # Simulated Annealing parameters
83
+ initial_temperature: float = 0.005
84
+ cooling_rate: float = 0.9999 # Slightly slower cooling for deeper search
85
+ max_iterations: int = 20000 # Increased iterations
86
+ initial_perturb_scale: float = 0.015
87
+ perturb_temp_power: float = 1.0 # Exponent for temperature scaling of perturbation
88
+ circles_to_perturb: int = 2
89
+ global_nudge_probability: float = 0.01 # Probability of applying a global nudge
90
+ global_nudge_amount: float = 0.005 # Max magnitude of global nudge
91
+ global_nudge_temp_power: float = 0.5 # Global nudge scales with temperature too
92
+
93
+ clip_epsilon: float = 1e-8
94
+
95
+
96
+ class CirclePackingState:
97
+ """
98
+ Represents a single state in the Simulated Annealing process, holding
99
+ circle centers and their calculated radii and sum of radii.
100
+ """
101
+ def __init__(self, centers: np.ndarray):
102
+ self.centers = centers
103
+ self.radii = compute_max_radii(self.centers)
104
+ self.sum_radii = np.sum(self.radii)
105
+
106
+ def copy(self):
107
+ """Creates a deep copy of the current state."""
108
+ return CirclePackingState(np.copy(self.centers))
109
+
110
+
111
+ class SimulatedAnnealingOptimizer:
112
+ """
113
+ Performs Simulated Annealing to find an optimal circle packing.
114
+ """
115
+ def __init__(self, config: CirclePackingConfig):
116
+ self.config = config
117
+ self.best_state = None
118
+ self.current_temperature = self.config.initial_temperature
119
+
120
+ def _generate_initial_centers(self) -> np.ndarray:
121
+ """
122
+ Generates the initial set of circle centers based on the configuration.
123
+ This combines the grid and central pair placement strategies.
124
+ """
125
+ initial_grid_centers = []
126
+ for i in range(self.config.grid_dim):
127
+ for j in range(self.config.grid_dim):
128
+ if i == self.config.grid_dim // 2 and j == self.config.grid_dim // 2:
129
+ continue
130
+ initial_grid_centers.append([self.config.grid_x_coords[i], self.config.grid_y_coords[j]])
131
+
132
+ # Calculate central pair's effective midpoint
133
+ effective_mid_x = 0.5 + self.config.central_midpoint_offset_x
134
+ effective_mid_y = 0.5 + self.config.central_midpoint_offset_y
135
+
136
+ # Calculate half-distance for each central circle
137
+ R_prime = self.config.central_separation_distance / 2.0
138
+ orientation_rad = math.radians(self.config.central_pair_orientation_angle_deg)
139
+
140
+ # Calculate raw displacement components
141
+ dx_raw = R_prime * math.cos(orientation_rad)
142
+ dy_raw = R_prime * math.sin(orientation_rad)
143
+
144
+ # Apply anisotropic scaling
145
+ dx = dx_raw * self.config.central_x_offset_scale
146
+ dy = dy_raw * self.config.central_y_offset_scale
147
+
148
+ initial_central_centers = np.array([
149
+ [effective_mid_x - dx, effective_mid_y - dy],
150
+ [effective_mid_x + dx, effective_mid_y + dy]
151
+ ])
152
+
153
+ initial_centers = np.vstack((initial_grid_centers, initial_central_centers))
154
+
155
+ # Apply global rotation to the initial state if configured
156
+ if abs(self.config.initial_global_rotation_angle_deg) > 1e-6:
157
+ angle_rad = math.radians(self.config.initial_global_rotation_angle_deg)
158
+ cos_theta, sin_theta = math.cos(angle_rad), math.sin(angle_rad)
159
+ rotation_matrix = np.array([[cos_theta, -sin_theta], [sin_theta, cos_theta]])
160
+ translated_centers = initial_centers - 0.5
161
+ rotated_translated_centers = (rotation_matrix @ translated_centers.T).T
162
+ initial_centers = rotated_translated_centers + 0.5
163
+
164
+ initial_centers = np.clip(initial_centers, self.config.clip_epsilon, 1 - self.config.clip_epsilon)
165
+ return initial_centers
166
+
167
+ def _perturb_centers(self, centers: np.ndarray) -> np.ndarray:
168
+ """
169
+ Applies local perturbations to a subset of circle centers.
170
+ The magnitude of perturbation scales with temperature.
171
+ """
172
+ new_centers = np.copy(centers)
173
+
174
+ # Perturbation scale decreases with temperature
175
+ perturb_amount = self.config.initial_perturb_scale * (self.current_temperature / self.config.initial_temperature)**self.config.perturb_temp_power
176
+
177
+ for _ in range(self.config.circles_to_perturb):
178
+ circle_idx = random.randint(0, self.config.n_circles - 1)
179
+
180
+ dx = random.uniform(-perturb_amount, perturb_amount)
181
+ dy = random.uniform(-perturb_amount, perturb_amount)
182
+
183
+ new_centers[circle_idx, 0] += dx
184
+ new_centers[circle_idx, 1] += dy
185
+
186
+ # Ensure centers stay within unit square boundaries
187
+ new_centers[circle_idx, 0] = np.clip(new_centers[circle_idx, 0], self.config.clip_epsilon, 1 - self.config.clip_epsilon)
188
+ new_centers[circle_idx, 1] = np.clip(new_centers[circle_idx, 1], self.config.clip_epsilon, 1 - self.config.clip_epsilon)
189
+
190
+ return new_centers
191
+
192
+ def _apply_global_nudge(self, centers: np.ndarray) -> np.ndarray:
193
+ """
194
+ Applies a small global offset to all circles with a certain probability.
195
+ Magnitude scales with temperature.
196
+ """
197
+ if random.random() < self.config.global_nudge_probability:
198
+ global_nudge_scale = self.config.global_nudge_amount * (self.current_temperature / self.config.initial_temperature)**self.config.global_nudge_temp_power
199
+ global_dx = random.uniform(-global_nudge_scale, global_nudge_scale)
200
+ global_dy = random.uniform(-global_nudge_scale, global_nudge_scale)
201
+
202
+ centers[:, 0] += global_dx
203
+ centers[:, 1] += global_dy
204
+ centers = np.clip(centers, self.config.clip_epsilon, 1 - self.config.clip_epsilon)
205
+ return centers
206
+
207
+ def optimize(self) -> (np.ndarray, np.ndarray):
208
+ """
209
+ Runs the Simulated Annealing optimization process.
210
+ Returns the centers and radii of the best packing found.
211
+ """
212
+ initial_centers = self._generate_initial_centers()
213
+ current_state = CirclePackingState(initial_centers)
214
+ self.best_state = current_state.copy()
215
+
216
+ for iteration in range(self.config.max_iterations):
217
+ # Create a new candidate state by perturbing current centers
218
+ candidate_centers = self._perturb_centers(current_state.centers)
219
+ candidate_centers = self._apply_global_nudge(candidate_centers) # Apply global nudge if probabilistic check passes
220
+
221
+ candidate_state = CirclePackingState(candidate_centers)
222
+
223
+ # Determine if the new state is accepted
224
+ delta_E = candidate_state.sum_radii - current_state.sum_radii # Maximizing sum_radii, so positive delta_E is good.
225
+
226
+ if delta_E > 0: # Always accept improvements
227
+ current_state = candidate_state
228
+ if current_state.sum_radii > self.best_state.sum_radii:
229
+ self.best_state = current_state.copy()
230
+ elif self.current_temperature > 0: # Accept worse solutions with probability
231
+ acceptance_probability = math.exp(delta_E / self.current_temperature)
232
+ if random.random() < acceptance_probability:
233
+ current_state = candidate_state
234
+
235
+ # Cool down the system
236
+ self.current_temperature *= self.config.cooling_rate
237
+
238
+ # Optional: Print progress
239
+ # if iteration % (self.config.max_iterations // 10) == 0:
240
+ # print(f"Iter {iteration}/{self.config.max_iterations}, Temp: {self.current_temperature:.6f}, Current Sum R: {current_state.sum_radii:.4f}, Best Sum R: {self.best_state.sum_radii:.4f}")
241
+
242
+ return self.best_state.centers, self.best_state.radii
243
+
244
+
245
+ def construct_packing():
246
+ """
247
+ Main entry point for constructing the packing.
248
+ Initializes a CirclePackingConfig with optimized parameters and uses
249
+ SimulatedAnnealingOptimizer to find the best packing.
250
+ """
251
+ # Optimized configuration parameters, a refinement of the previously successful SA run.
252
+ # Tuned grid_x/y_coords slightly, tightened initial perturb scale, slowed cooling.
253
+ config = CirclePackingConfig(
254
+ n_circles=26,
255
+ grid_x_coords=(0.10, 0.30, 0.50, 0.70, 0.90), # Retained uniform grid points
256
+ grid_y_coords=(0.10, 0.30, 0.50, 0.70, 0.90), # Retained uniform grid points
257
+ grid_dim=5,
258
+
259
+ central_separation_distance=0.125,
260
+ central_pair_orientation_angle_deg=44.5,
261
+ central_midpoint_offset_x=-0.0015,
262
+ central_midpoint_offset_y=0.0010,
263
+ central_x_offset_scale=1.01,
264
+ central_y_offset_scale=0.99,
265
+ initial_global_rotation_angle_deg=0.1,
266
+
267
+ initial_temperature=0.005,
268
+ cooling_rate=0.9999, # Slightly slower cooling
269
+ max_iterations=20000, # Increased iterations for deeper search
270
+ initial_perturb_scale=0.015,
271
+ perturb_temp_power=1.0, # Linear scaling
272
+ circles_to_perturb=2,
273
+ global_nudge_probability=0.01,
274
+ global_nudge_amount=0.005,
275
+ global_nudge_temp_power=0.5, # Global nudge scales slower with temp decay
276
+ clip_epsilon=1e-8
277
+ )
278
+
279
+ optimizer = SimulatedAnnealingOptimizer(config)
280
+ centers, radii = optimizer.optimize()
281
+
282
+ return centers, radii
283
+ # EVOLVE-BLOCK-END
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/best/search_replace.txt ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Based on my analysis of previous high-performing solutions and the current code's structure, the most promising path to a higher score is to re-introduce a Simulated Annealing (SA) optimization stage. The current deterministic constructor provides an excellent starting point, but an iterative refinement process is necessary to explore the solution space more thoroughly and escape local optima.
2
+
3
+ My proposed edits will integrate the powerful SA logic from a previous successful generation (which scored 2.58) into the cleaner, class-based architecture of the current program. This involves three main changes:
4
+
5
+ 1. **Enhancing `PackingConfig`**: I will add SA-specific parameters (like temperature, cooling rate, iterations, and perturbation scales) to the configuration dataclass. I will also fine-tune the initial placement parameters to match those of a previous high-scoring run, reintroducing subtle asymmetries (y-offset, global rotation) that have proven effective at breaking symmetry and unlocking better packings.
6
+ 2. **Upgrading `CirclePackingSolver`**: I will transform the current deterministic solver into a full-fledged SA optimizer. The `solve` method will now execute the SA loop, starting with the strong initial configuration and iteratively perturbing the circle centers to search for a better arrangement. This involves adding helper methods for perturbation and probabilistic global nudges, which are key SA strategies.
7
+ 3. **Updating Documentation**: I will update the docstrings to accurately reflect the new optimization strategy, making the code's intent clearer.
8
+
9
+ By combining a strong initial guess with a robust optimization algorithm, we give the solver the best chance to find a packing with a sum of radii closer to the theoretical maximum.
10
+
11
+ <NAME>
12
+ reintroduce_simulated_annealing
13
+ </NAME>
14
+
15
+ <DESCRIPTION>
16
+ This edit re-introduces a Simulated Annealing (SA) optimization process into the `CirclePackingSolver`. The current deterministic solver is enhanced to become an SA optimizer. The `PackingConfig` is updated to include SA parameters (temperature, cooling rate, iterations, etc.) and refined initial placement values based on previous high-scoring runs. The `CirclePackingSolver.solve` method is rewritten to perform the SA loop: it generates a strong initial configuration and then iteratively perturbs it, accepting better states or worse ones with a certain probability, to search for a globally better packing. This combines the clean structure of the current code with the proven optimization power of SA.
17
+ </DESCRIPTION>
18
+
19
+ <DIFF>
20
+ <<<<<<< SEARCH
21
+ from dataclasses import dataclass
22
+ import math
23
+ =======
24
+ from dataclasses import dataclass
25
+ import math
26
+ import random
27
+ >>>>>>> REPLACE
28
+ </DIFF>
29
+
30
+ <DIFF>
31
+ <<<<<<< SEARCH
32
+ @dataclass
33
+ class PackingConfig:
34
+ """
35
+ A unified configuration object holding all tunable parameters for the circle packing problem.
36
+ This dataclass consolidates the most effective parameters from previous successful attempts,
37
+ including non-uniform grids, central pair offsets, and anisotropic scaling.
38
+ """
39
+ n_circles: int = 26
40
+
41
+ # Grid parameters allowing non-uniform spacing.
42
+ # The default is the robust 5x5 grid that has performed well.
43
+ grid_x_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
44
+ grid_y_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
45
+
46
+ # Central circle parameters. These are tuned based on the best-performing prior program (score 2.52).
47
+ # Distance between the two central circle centers.
48
+ central_separation_distance: float = 0.125
49
+ # The angle of the line connecting the two central circles.
50
+ central_pair_orientation_angle_deg: float = 44.5
51
+ # The distance to shift the entire central pair's midpoint from (0.5, 0.5).
52
+ central_midpoint_offset_dist: float = 0.0015
53
+ # The angle of the midpoint shift. 180 degrees corresponds to a negative x-offset.
54
+ central_midpoint_offset_angle_deg: float = 180.0
55
+ # Anisotropic scaling to create an elliptical void for the central pair.
56
+ central_x_offset_scale: float = 1.01
57
+ central_y_offset_scale: float = 0.99
58
+
59
+ # Global rotation for the entire packing arrangement.
60
+ global_rotation_angle_deg: float = 0.0
61
+
62
+ clip_epsilon: float = 1e-8
63
+ =======
64
+ @dataclass
65
+ class PackingConfig:
66
+ """
67
+ A unified configuration object holding all tunable parameters for the circle packing problem.
68
+ This dataclass consolidates parameters for initial placement and adds parameters for
69
+ Simulated Annealing (SA) to refine the packing.
70
+ """
71
+ n_circles: int = 26
72
+
73
+ # Grid parameters for the initial 24 circles.
74
+ grid_x_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
75
+ grid_y_coords: tuple = (0.1, 0.3, 0.5, 0.7, 0.9)
76
+
77
+ # Central circle parameters for initial placement, tuned from high-scoring runs.
78
+ central_separation_distance: float = 0.125
79
+ central_pair_orientation_angle_deg: float = 44.5
80
+ # Asymmetric offset for the central pair's midpoint.
81
+ central_midpoint_offset_dist: float = 0.0018 # From x=-0.0015, y=0.0010
82
+ central_midpoint_offset_angle_deg: float = 146.3 # From x=-0.0015, y=0.0010
83
+ # Anisotropic scaling for the central pair's void.
84
+ central_x_offset_scale: float = 1.01
85
+ central_y_offset_scale: float = 0.99
86
+
87
+ # Global rotation for the initial packing arrangement.
88
+ global_rotation_angle_deg: float = 0.1
89
+
90
+ # Simulated Annealing (SA) parameters
91
+ initial_temperature: float = 0.005
92
+ cooling_rate: float = 0.9999
93
+ max_iterations: int = 25000
94
+ initial_perturb_scale: float = 0.015
95
+ perturb_temp_power: float = 1.0
96
+ circles_to_perturb: int = 2
97
+ global_nudge_probability: float = 0.01
98
+ global_nudge_amount: float = 0.005
99
+ global_nudge_temp_power: float = 0.5
100
+
101
+ clip_epsilon: float = 1e-8
102
+ >>>>>>> REPLACE
103
+ </DIFF>
104
+
105
+ <DIFF>
106
+ <<<<<<< SEARCH
107
+ class CirclePackingSolver:
108
+ """
109
+ An encapsulated solver for the circle packing problem.
110
+ This class takes a configuration object and orchestrates the generation of
111
+ circle centers and the computation of their optimal radii. This new structure
112
+ centralizes all logic into a single, reusable class.
113
+ """
114
+
115
+ def __init__(self, config: PackingConfig):
116
+ """Initializes the solver with a given packing configuration."""
117
+ self.config = config
118
+ self.centers = None
119
+ self.radii = None
120
+
121
+ def _generate_grid_centers(self) -> np.ndarray:
122
+ """Generates centers for 24 circles arranged in a grid, skipping the central point."""
123
+ grid_centers = []
124
+ num_divs = len(self.config.grid_x_coords)
125
+ for i in range(num_divs):
126
+ for j in range(num_divs):
127
+ if i == num_divs // 2 and j == num_divs // 2:
128
+ continue
129
+ grid_centers.append([self.config.grid_x_coords[i], self.config.grid_y_coords[j]])
130
+ return np.array(grid_centers)
131
+
132
+ def _generate_central_centers(self) -> np.ndarray:
133
+ """
134
+ Generates centers for the 2 central circles using a flexible parameterization
135
+ that includes midpoint offset, orientation, and anisotropic scaling.
136
+ """
137
+ # Calculate the central pair's midpoint with an offset from (0.5, 0.5).
138
+ offset_rad = math.radians(self.config.central_midpoint_offset_angle_deg)
139
+ mid_x = 0.5 + self.config.central_midpoint_offset_dist * math.cos(offset_rad)
140
+ mid_y = 0.5 + self.config.central_midpoint_offset_dist * math.sin(offset_rad)
141
+
142
+ # Calculate the displacement vectors for the two circles from their midpoint.
143
+ R_prime = self.config.central_separation_distance / 2.0
144
+ orientation_rad = math.radians(self.config.central_pair_orientation_angle_deg)
145
+
146
+ # Raw displacement based on orientation angle.
147
+ dx_raw = R_prime * math.cos(orientation_rad)
148
+ dy_raw = R_prime * math.sin(orientation_rad)
149
+
150
+ # Apply anisotropic scaling.
151
+ dx = dx_raw * self.config.central_x_offset_scale
152
+ dy = dy_raw * self.config.central_y_offset_scale
153
+
154
+ return np.array([
155
+ [mid_x - dx, mid_y - dy],
156
+ [mid_x + dx, mid_y + dy]
157
+ ])
158
+
159
+ def _apply_global_rotation(self, centers: np.ndarray) -> np.ndarray:
160
+ """Applies a global rotation to all circle centers around (0.5, 0.5)."""
161
+ if abs(self.config.global_rotation_angle_deg) < 1e-6:
162
+ return centers
163
+
164
+ angle_rad = math.radians(self.config.global_rotation_angle_deg)
165
+ cos_theta, sin_theta = math.cos(angle_rad), math.sin(angle_rad)
166
+ rotation_matrix = np.array([[cos_theta, -sin_theta], [sin_theta, cos_theta]])
167
+
168
+ # Translate to origin, rotate, then translate back.
169
+ return (rotation_matrix @ (centers - 0.5).T).T + 0.5
170
+
171
+ def solve(self) -> tuple[np.ndarray, np.ndarray]:
172
+ """
173
+ Executes the full packing and solving pipeline.
174
+ 1. Generates centers for all circles.
175
+ 2. Applies global transformations.
176
+ 3. Computes the maximum possible radii using linear programming.
177
+ """
178
+ grid_centers = self._generate_grid_centers()
179
+ central_centers = self._generate_central_centers()
180
+
181
+ all_centers = np.vstack((grid_centers, central_centers))
182
+ all_centers = self._apply_global_rotation(all_centers)
183
+
184
+ # Clip to ensure centers are strictly inside the unit square.
185
+ self.centers = np.clip(all_centers, self.config.clip_epsilon, 1 - self.config.clip_epsilon)
186
+
187
+ self.radii = self._compute_max_radii(self.centers)
188
+
189
+ return self.centers, self.radii
190
+ =======
191
+ class CirclePackingSolver:
192
+ """
193
+ An encapsulated solver for the circle packing problem, now enhanced with
194
+ a Simulated Annealing (SA) optimization loop. It starts with a strong
195
+ initial configuration and iteratively refines it to find a better packing.
196
+ """
197
+
198
+ def __init__(self, config: PackingConfig):
199
+ """Initializes the solver with a given packing configuration."""
200
+ self.config = config
201
+ self.current_temperature = config.initial_temperature
202
+
203
+ def _generate_initial_centers(self) -> np.ndarray:
204
+ """Generates the initial set of circle centers based on the configuration."""
205
+ # 1. Generate grid centers
206
+ grid_centers = []
207
+ num_divs = len(self.config.grid_x_coords)
208
+ for i in range(num_divs):
209
+ for j in range(num_divs):
210
+ if i == num_divs // 2 and j == num_divs // 2:
211
+ continue
212
+ grid_centers.append([self.config.grid_x_coords[i], self.config.grid_y_coords[j]])
213
+
214
+ # 2. Generate central centers
215
+ offset_rad = math.radians(self.config.central_midpoint_offset_angle_deg)
216
+ mid_x = 0.5 + self.config.central_midpoint_offset_dist * math.cos(offset_rad)
217
+ mid_y = 0.5 + self.config.central_midpoint_offset_dist * math.sin(offset_rad)
218
+
219
+ R_prime = self.config.central_separation_distance / 2.0
220
+ orientation_rad = math.radians(self.config.central_pair_orientation_angle_deg)
221
+
222
+ dx_raw = R_prime * math.cos(orientation_rad)
223
+ dy_raw = R_prime * math.sin(orientation_rad)
224
+
225
+ dx = dx_raw * self.config.central_x_offset_scale
226
+ dy = dy_raw * self.config.central_y_offset_scale
227
+
228
+ central_centers = np.array([
229
+ [mid_x - dx, mid_y - dy],
230
+ [mid_x + dx, mid_y + dy]
231
+ ])
232
+
233
+ all_centers = np.vstack((grid_centers, central_centers))
234
+
235
+ # 3. Apply global rotation
236
+ if abs(self.config.global_rotation_angle_deg) > 1e-6:
237
+ angle_rad = math.radians(self.config.global_rotation_angle_deg)
238
+ cos_theta, sin_theta = math.cos(angle_rad), math.sin(angle_rad)
239
+ rotation_matrix = np.array([[cos_theta, -sin_theta], [sin_theta, cos_theta]])
240
+ all_centers = (rotation_matrix @ (all_centers - 0.5).T).T + 0.5
241
+
242
+ return np.clip(all_centers, self.config.clip_epsilon, 1 - self.config.clip_epsilon)
243
+
244
+ def _perturb_centers(self, centers: np.ndarray) -> np.ndarray:
245
+ """Applies local perturbations to a subset of circle centers."""
246
+ new_centers = np.copy(centers)
247
+ perturb_amount = self.config.initial_perturb_scale * (self.current_temperature / self.config.initial_temperature)**self.config.perturb_temp_power
248
+
249
+ for _ in range(self.config.circles_to_perturb):
250
+ circle_idx = random.randint(0, self.config.n_circles - 1)
251
+
252
+ dx = random.uniform(-perturb_amount, perturb_amount)
253
+ dy = random.uniform(-perturb_amount, perturb_amount)
254
+
255
+ new_centers[circle_idx, 0] += dx
256
+ new_centers[circle_idx, 1] += dy
257
+
258
+ new_centers[circle_idx, :] = np.clip(new_centers[circle_idx, :], self.config.clip_epsilon, 1 - self.config.clip_epsilon)
259
+
260
+ return new_centers
261
+
262
+ def _apply_global_nudge(self, centers: np.ndarray) -> np.ndarray:
263
+ """Applies a small global offset to all circles with a certain probability."""
264
+ if random.random() < self.config.global_nudge_probability:
265
+ nudge_scale = self.config.global_nudge_amount * (self.current_temperature / self.config.initial_temperature)**self.config.global_nudge_temp_power
266
+ global_dx = random.uniform(-nudge_scale, nudge_scale)
267
+ global_dy = random.uniform(-nudge_scale, nudge_scale)
268
+ centers[:, 0] += global_dx
269
+ centers[:, 1] += global_dy
270
+ centers = np.clip(centers, self.config.clip_epsilon, 1 - self.config.clip_epsilon)
271
+ return centers
272
+
273
+ def solve(self) -> tuple[np.ndarray, np.ndarray]:
274
+ """
275
+ Executes the full SA optimization pipeline.
276
+ 1. Generates a strong initial configuration.
277
+ 2. Iteratively perturbs centers and accepts new states based on SA logic.
278
+ 3. Returns the best configuration found.
279
+ """
280
+ current_centers = self._generate_initial_centers()
281
+ current_sum_radii = np.sum(self._compute_max_radii(current_centers))
282
+
283
+ best_centers = np.copy(current_centers)
284
+ best_sum_radii = current_sum_radii
285
+
286
+ for _ in range(self.config.max_iterations):
287
+ candidate_centers = self._perturb_centers(current_centers)
288
+ candidate_centers = self._apply_global_nudge(candidate_centers)
289
+
290
+ candidate_sum_radii = np.sum(self._compute_max_radii(candidate_centers))
291
+
292
+ delta_E = candidate_sum_radii - current_sum_radii
293
+
294
+ if delta_E > 0 or (self.current_temperature > 0 and random.random() < math.exp(delta_E / self.current_temperature)):
295
+ current_centers = candidate_centers
296
+ current_sum_radii = candidate_sum_radii
297
+ if current_sum_radii > best_sum_radii:
298
+ best_centers = np.copy(current_centers)
299
+ best_sum_radii = current_sum_radii
300
+
301
+ self.current_temperature *= self.config.cooling_rate
302
+
303
+ final_radii = self._compute_max_radii(best_centers)
304
+ return best_centers, final_radii
305
+ >>>>>>> REPLACE
306
+ </DIFF>
307
+
308
+ <DIFF>
309
+ <<<<<<< SEARCH
310
+ def construct_packing():
311
+ """
312
+ Main entry point for constructing the circle packing.
313
+ This function now uses the unified CirclePackingSolver class, demonstrating the
314
+ new, cleaner architecture. The configuration is instantiated with parameters
315
+ derived from the best-performing historical programs to aim for a new high score.
316
+ """
317
+ # This configuration is a refined combination of the best parameters observed
318
+ # in previous high-scoring runs (scores > 2.51).
319
+ config = PackingConfig()
320
+
321
+ # Create a solver instance with the specified configuration.
322
+ solver = CirclePackingSolver(config)
323
+
324
+ # Run the solver to get the final centers and radii.
325
+ centers, radii = solver.solve()
326
+
327
+ return centers, radii
328
+ =======
329
+ def construct_packing():
330
+ """
331
+ Main entry point for constructing the circle packing.
332
+ This function uses the CirclePackingSolver, which implements a Simulated
333
+ Annealing (SA) algorithm. It starts with a strong initial configuration and
334
+ then uses SA to search for improvements.
335
+ """
336
+ # The configuration is instantiated with default parameters that include
337
+ # both the initial placement logic and the SA optimization parameters.
338
+ # These defaults are tuned from previous high-scoring runs.
339
+ config = PackingConfig()
340
+
341
+ # Create a solver instance with the specified configuration.
342
+ solver = CirclePackingSolver(config)
343
+
344
+ # Run the solver to get the final centers and radii.
345
+ centers, radii = solver.solve()
346
+
347
+ return centers, radii
348
+ >>>>>>> REPLACE
349
+ </DIFF>
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/eval_agent_memory/EVAL_AGENTS.md ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Generation 164 Evaluation
2
+
3
+ **Auxiliary Metrics Implemented:**
4
+
5
+ This generation is in the **OPTIMIZATION/CONVERGENCE** stage. The focus is on refining solutions and understanding the nuanced characteristics of high-performing packings. The primary metric maximizes the sum of radii, ensuring no overlaps or boundary violations. These auxiliary metrics aim to provide insights into *how* that maximum is achieved and what structural properties define optimal or near-optimal solutions.
6
+
7
+ **Group 1: Radii Statistics**
8
+ * **`avg_radius`**: The average radius of all 26 circles. _Useful for tracking whether the evolution favors uniformly sized circles or allows for a wide range of sizes. Helps to identify trends in average circle size during optimization._
9
+ * **`std_dev_radius`**: The standard deviation of the radii. _A higher value indicates greater diversity in circle sizes, while a lower value suggests a more uniform distribution. This can reveal different packing strategies (e.g., many small vs. few large and few small)._
10
+ * **`min_radius`**: The smallest radius among all circles. _Indicates if the solution relies on very small circles to fill gaps._
11
+ * **`max_radius`**: The largest radius among all circles. _Indicates the size of the dominant circles in the packing._
12
+ * **`median_radius`**: The median radius of all circles. _Provides a robust measure of central tendency for radii, less sensitive to outliers than the mean. Useful for understanding the typical circle size._
13
+ * **`num_unique_radii`**: The count of unique radius values used. _A high number indicates greater variety in circle sizes, potentially suggesting more complex packing patterns._
14
+
15
+ **Group 2: Area Coverage Metrics**
16
+ * **`total_area_covered`**: The sum of the areas (pi * r^2) of all 26 circles. _This provides a direct measure of the physical space occupied by the circles, complementing the primary metric (sum of radii) by showing the actual 2D coverage._
17
+ * **`packing_density`**: The ratio of `total_area_covered` to the unit square's area (which is 1.0). _A normalized measure of how efficiently the square is filled. Higher is better._
18
+ * **`empty_space_ratio`**: The proportion of the unit square's area not covered by circles (`1 - packing_density`). _This should ideally be minimized in optimal solutions._
19
+
20
+ **Group 3: Violation Metrics (Magnitude & Count)**
21
+ * **`max_circle_overlap_magnitude`**: The largest positive value (sum of radii - distance between centers) for any overlapping pair. _Even if the primary evaluator reports no overlap (due to internal tolerances), a small positive value here indicates a "near miss" or a potential instability. Should be 0 for truly valid solutions._
22
+ * **`num_overlapping_pairs`**: The total count of pairs of circles that overlap. _Should be 0 for valid solutions. If > 0, it highlights invalid solutions and indicates the severity of the overlap problem._
23
+ * **`max_boundary_violation_magnitude`**: The largest extent to which any circle protrudes beyond the unit square boundary. _Similar to overlap magnitude, it identifies near-boundary violations or slight excursions. Should be 0 for truly valid solutions._
24
+ * **`num_boundary_violations`**: The total count of circles that violate any boundary. _Should be 0 for valid solutions. If > 0, indicates an invalid solution._
25
+
26
+ **Group 4: Advanced Spatial and Contact Metrics**
27
+ * **`packing_aspect_ratio_of_centers_bbox`**: The aspect ratio of the bounding box that encloses all circle centers. _Reveals the overall shape of the packing arrangement. Values closer to 1 indicate a more square-like distribution of centers, while larger values indicate an elongated or linear arrangement._
28
+ * **`avg_num_touching_neighbors`**: The average number of other circles a given circle is "touching" (within a small tolerance). _Higher values suggest a more tightly packed and interlocked arrangement, often seen in optimal configurations._
29
+ * **`avg_quadrant_radii_std_dev`**: The average of the standard deviations of radii calculated for each of the four quadrants of the unit square. _This helps to understand if radius diversity is localized (e.g., all small circles in one corner) or evenly distributed._
30
+
31
+ **Group 5: Center Distribution Metrics**
32
+ * **`avg_pairwise_center_distance`**: The average Euclidean distance between the centers of all unique pairs of circles. _A lower value might indicate a more clustered or dense arrangement of circles._
33
+ * **`avg_distance_from_packing_centroid_normalized`**: The average distance of each circle's center from the overall centroid (average position) of all circle centers, normalized by the maximum possible distance from the unit square center to a corner. _Lower values indicate that the packing is more centrally concentrated, while higher values suggest a more dispersed pattern._
34
+
35
+ **Group 6: Boundary Contact Metrics**
36
+ * **`num_circles_touching_edge`**: The count of circles whose outer edge is tangent to or nearly touching any of the four boundaries of the unit square (within a small tolerance). _Higher values suggest better utilization of the perimeter of the packing space._
37
+ * **`num_circles_touching_corner`**: The count of circles whose outer edge is tangent to or nearly touching two orthogonal boundaries (i.e., a corner) of the unit square. _A more specific measure of boundary utilization, indicating how well corners are filled._
38
+
39
+ **Group 7: Boundary Utilization (Distance)**
40
+ * **`avg_min_circle_to_boundary_distance`**: The average of the minimum distances from each circle's edge to the closest boundary. _Smaller positive values indicate a tighter fit of circles against the edges, while larger values suggest more "cushion" space._
41
+
42
+ **Observations for Generation 164 and Recommendations:**
43
+
44
+ * **Current Score (Primary Metric):** 2.3750. This is a good reference point for comparative analysis.
45
+ * **Evolution Stage:** Optimization/Convergence. At this stage, solutions should ideally have `num_overlapping_pairs` and `num_boundary_violations` as 0. Small non-zero values for `max_circle_overlap_magnitude` or `max_boundary_violation_magnitude` might indicate fragility or solutions that are barely meeting constraints.
46
+ * **Insights from Auxiliary Metrics (Expected):**
47
+ * High `packing_density` and low `empty_space_ratio` are expected for high primary scores.
48
+ * `avg_num_touching_neighbors` could provide insights into the connectivity of the packing; higher values often correlate with denser packing.
49
+ * `std_dev_radius` and `num_unique_radii` will indicate if the optimal solutions are favoring uniform sizes or exploiting diverse sizes.
50
+ * `avg_min_circle_to_boundary_distance` will show how aggressively circles are placed near the boundaries.
51
+
52
+ **Recommendations:**
53
+ * Monitor `max_circle_overlap_magnitude` and `max_boundary_violation_magnitude`: Even if passing, persistently small positive values could indicate a fragile solution. The evolution should aim for values very close to zero or negative (meaning separation from boundaries/other circles).
54
+ * Analyze trends in `std_dev_radius` and `num_unique_radii`: Are optimal solutions becoming more uniform or more diverse in radii? This can inform further genetic operations.
55
+ * Correlate `avg_num_touching_neighbors` with the primary score: A strong positive correlation would suggest that increasing local density (more touching circles) is a key driver for higher overall radii sums.
56
+ * Observe `avg_distance_from_packing_centroid_normalized`: If this metric stays high, it could mean solutions are concentrating circles in corners or along edges, potentially leaving a large empty space in the middle. If it's low, it suggests a more centralized packing.
57
+
58
+ ## Generation 165 Evaluation
59
+
60
+ **Auxiliary Metrics Implemented:**
61
+
62
+ **Group 1: Radii Statistics**
63
+ * **`num_unique_radii`**: The count of distinct radius values among the 26 circles. This metric provides insight into the diversity of circle sizes being used in the packing solution. A higher number of unique radii can indicate a more complex or adaptive packing strategy, potentially filling space more efficiently by using a wider range of circle sizes. A lower number might suggest a simpler strategy focusing on a few dominant sizes.
64
+
65
+ **Observations and Recommendations for Generation 165:**
66
+
67
+ At Generation 165, the evolution is likely in the **CONVERGENCE** or **PLATEAU** stage. The primary metric (sum of radii) may have stabilized, and we need to identify subtle characteristics that could lead to further improvements or signal stagnation.
68
+
69
+ * **`num_unique_radii`**: This metric is particularly useful at this late stage. If `num_unique_radii` is consistently low (e.g., 1 or 2 distinct sizes), it could indicate that the evolution has found a local optimum that relies on a very uniform or simple size distribution. Encouraging a higher diversity in radii might help break this plateau, allowing the algorithm to explore solutions with a wider range of circle sizes that could fit into complex gaps. Conversely, if `num_unique_radii` is already high, it suggests the algorithm is already exploring diverse sizes effectively.
70
+
71
+ By tracking this metric, we can gain insight into the inherent complexity of the radius distributions that the evolution is converging upon, and potentially guide it towards strategies that leverage more varied sizing for better packing.
72
+
73
+ # Evaluation Agent Memory
74
+
75
+ This file tracks the auxiliary evaluation metrics and insights generated across different evolution generations.
76
+
77
+ ## Generation 19 Evaluation
78
+
79
+ **Auxiliary Metrics Implemented:**
80
+
81
+ **Group 1: Packing Efficiency**
82
+ * **`total_area_covered`**: Sum of the areas of all 26 circles (pi * r^2 for each). This measures how much of the unit square is occupied by the circles. A higher value indicates better space utilization, focusing on the actual "filled" area rather than just the sum of radii (which is the primary score). Range 0 to ~1 (or slightly more if circles could overlap, but primary prevents that).
83
+ * **`packing_density`**: This is identical to `total_area_covered` because the unit square has an area of 1. It directly represents the percentage of the available area covered by circles. Higher is better, indicating more efficient use of space.
84
+
85
+ **Group 2: Boundary Utilization**
86
+ * **`avg_min_distance_to_boundary`**: The average of the minimum distances from the edge of each circle to any of the four boundaries of the unit square. Smaller values (closer to 0) indicate that circles are packed more tightly against the boundaries, which is often a characteristic of optimal packing solutions. Negative values would imply a violation (circle outside boundary) which the primary metric already checks. Useful for understanding how well the solution leverages the container edges.
87
+ * **`min_overall_distance_to_boundary`**: The single smallest distance from any circle's edge to any boundary. This metric highlights if at least one circle is positioned extremely close to a boundary, indicating a strategy to maximize space.
88
+
89
+ **Group 3: Radii Statistics**
90
+ * **`avg_radius`**: The average radius of the 26 circles. This gives an idea of the typical size of circles in the packing. Can indicate if solutions are favoring uniform or varied circle sizes.
91
+ * **`std_dev_radius`**: The standard deviation of the radii of the 26 circles. A higher standard deviation indicates greater diversity in circle sizes, while a lower one suggests more uniform sizes. This can reveal different packing strategies and encourage diversity.
92
+ * **`min_radius`**: The smallest radius among the 26 circles. Can indicate if the solution is introducing very small circles.
93
+ * **`max_radius`**: The largest radius among the 26 circles. Can indicate if the solution is trying to fit very large circles.
94
+
95
+ ## Generation 39 Evaluation
96
+
97
+ **Observations and Recommendations:**
98
+
99
+ For Generation 39, we continue to track the following auxiliary metrics, which complement the primary objective of maximizing `sum(radii)`:
100
+
101
+ * **Packing Efficiency (`total_area_covered`, `packing_density`):** These metrics provide insight into the actual physical space utilized by the circles. By comparing trends in `sum(radii)` (primary score) with `total_area_covered`, we can understand if improvements in `sum(radii)` are consistently translating to better overall area utilization. If not, it might indicate a need to adjust the optimization strategy to consider `r^2` more directly.
102
+
103
+ * **Radii Statistics (`avg_radius`, `std_dev_radius`, `min_radius`, `max_radius`):** At this stage, observing the `std_dev_radius` is crucial. If it's decreasing significantly, it might suggest the algorithm is converging on a specific distribution of circle sizes. If it remains high or fluctuates, it indicates continued exploration of diverse circle size combinations. Monitoring `min_radius` and `max_radius` can reveal if the solution is relying on very small (and potentially less impactful on `sum(radii)` but useful for filling gaps) or very large circles.
104
+
105
+ * **Boundary Utilization (`avg_min_distance_to_boundary`, `min_min_distance_to_boundary`):** These metrics are vital for assessing how aggressively the solutions are pushing circles against the unit square boundaries. Consistently lower (closer to zero or slightly negative, implying tight fit if primary evaluator doesn't flag violations) `avg_min_distance_to_boundary` values over generations would suggest a more efficient packing strategy, where circles are fully leveraging the available space. If these values are relatively high, it might indicate that circles are not being pushed close enough to the edges, leaving valuable space unused.
106
+
107
+ **Overall Strategy Recommendation:**
108
+
109
+ At this stage, we should monitor these auxiliary metrics for signs of stagnation or subtle improvements that the primary score might not fully capture. For instance, a plateau in the primary score coupled with stable or slightly improving `packing_density` and `avg_min_distance_to_boundary` could mean the algorithm is finding more robust or efficient ways to utilize space without significantly increasing the sum of radii. Conversely, a plateau in the primary score combined with stagnant auxiliary metrics might signal a local optimum, requiring a perturbation or shift in evolution strategy.
110
+
111
+
112
+
113
+
114
+ ## Generation 60 Evaluation
115
+
116
+ **Auxiliary Metrics Implemented:**
117
+
118
+
119
+ ## Generation 185 Evaluation
120
+
121
+ **Auxiliary Metrics Implemented:**
122
+
123
+ **Group 1: Radii Distribution Statistics (`_calculate_radii_stats`)**
124
+ * **`avg_radius`**: The arithmetic mean of all circle radii. Provides a central tendency for circle sizes.
125
+ * **`std_dev_radius`**: The standard deviation of all circle radii. Measures the dispersion or variability of circle sizes. A higher value indicates more diverse radii.
126
+ * **`min_radius`**: The smallest radius among all circles. Can highlight the presence of very small "filler" circles.
127
+ * **`max_radius`**: The largest radius among all circles. Indicates the size of the dominant circles.
128
+ * **`median_radius`**: The median radius of all circles. A robust measure of central tendency, less affected by outliers than the mean.
129
+ * **`num_unique_radii`**: The count of distinct radius values. A higher number suggests more variety in the chosen circle sizes.
130
+ * **`radius_coefficient_of_variation`**: The ratio of the standard deviation to the mean radius. A normalized measure of dispersion, useful for comparing variability across different scales of radii.
131
+
132
+ **Group 2: Area Coverage Metrics (`_calculate_area_metrics`)**
133
+ * **`total_area_covered`**: The sum of the areas of all circles ($\sum \pi r^2$). Provides a direct measure of the total physical space occupied by the circles. Directly correlates with the primary score (sum of radii) but gives insight into the actual area coverage.
134
+ * **`packing_density`**: `total_area_covered` divided by the area of the unit square (which is 1). Expresses the proportion of the unit square covered by circles (0-1 range). Higher is better.
135
+ * **`empty_space_ratio`**: 1 - `packing_density`. Represents the proportion of the unit square not covered by circles.
136
+
137
+ **Group 3: Boundary Proximity (`_calculate_boundary_proximity`)**
138
+ * **`num_circles_touching_boundary`**: Count of circles whose edge is within a small epsilon (1e-4) of any of the unit square boundaries (x=0, x=1, y=0, y=1). Indicates how many circles are actively utilizing the perimeter of the packing area.
139
+ * **`avg_min_distance_to_boundary`**: The average of the minimum distances from each circle's edge to its closest unit square boundary. Lower values indicate tighter packing against the container walls, which is generally desirable for dense packing.
140
+
141
+ **Group 4: Advanced Spatial and Contact Metrics (`_calculate_advanced_spatial_and_contact_metrics`)**
142
+ * **`packing_aspect_ratio_of_centers_bbox`**: The aspect ratio of the bounding box that encloses all circle centers. A value close to 1 indicates a more square-like distribution of centers, while values far from 1 suggest a more elongated or asymmetric arrangement. This can hint at the overall shape of the packing.
143
+ * **`avg_num_touching_neighbors`**: The average number of other circles a given circle is "touching" (within a tolerance of 1e-6). A higher value suggests a more interconnected and rigid packing structure, often associated with higher density.
144
+ * **`avg_quadrant_radii_std_dev`**: The average of the standard deviations of radii within each of the four quadrants of the unit square. This metric helps assess the uniformity of circle sizes across different regions of the packing. Lower values might indicate more homogeneous packing in different regions.
145
+
146
+ **Group 5: Pairwise Center Distances (`_calculate_pairwise_center_distances`)**
147
+ * **`avg_pairwise_center_distance`**: The average Euclidean distance between the centers of all unique pairs of circles. Provides a general measure of how spread out or clustered the circle centers are. Smaller values indicate more tightly clustered centers.
148
+
149
+ **Group 6: Center Distribution Metrics (`_calculate_center_distribution_metrics`)**
150
+ * **`avg_distance_from_packing_centroid_normalized`**: The average distance of each circle center from the centroid of all circle centers, normalized by the maximum possible distance from the unit square center to a corner ($\sqrt{0.5}$). Measures how concentrated or dispersed the circles are around their collective center. Lower values suggest a more centrally concentrated packing.
151
+
152
+ **Group 7: Boundary Contact Metrics (`_calculate_boundary_contact_metrics`)**
153
+ * **`num_circles_touching_edge`**: The count of circles that have at least one edge touching any of the four unit square boundaries (within a tolerance of 1e-6).
154
+ * **`num_circles_touching_corner`**: The count of circles that have edges touching two orthogonal boundaries, thus indicating contact with a corner of the unit square (within a tolerance of 1e-6).
155
+
156
+ **Insights and Recommendations for Generation 185:**
157
+
158
+ At Generation 185, the evolution is likely in the "Optimization" or "Convergence" phase. The primary metric focuses solely on maximizing the sum of radii (implicitly, the `total_area_covered`). These auxiliary metrics provide a multi-faceted view of the solutions.
159
+
160
+ * **Radii Distribution**: Analyzing `avg_radius`, `std_dev_radius`, and `num_unique_radii` can reveal if the evolutionary process is settling on a particular strategy regarding circle sizes (e.g., solutions with many small circles vs. a few large ones and some fillers). If the `std_dev_radius` is increasing at this late stage, it might suggest the model is still exploring diverse size combinations, which could be beneficial.
161
+ * **Packing Density and Boundary Utilization**: While `packing_density` directly relates to the primary score, `num_circles_touching_boundary` and `avg_min_distance_to_boundary` offer critical insight into *how* that density is achieved. Optimal solutions often tightly utilize boundaries. If `num_circles_touching_boundary` is low, or `avg_min_distance_to_boundary` is high, it indicates missed opportunities for denser packing by not fully leveraging the container edges.
162
+ * **Structural Efficiency**: `avg_num_touching_neighbors` is a key indicator of structural rigidity and efficiency. Higher values generally mean a more stable and tightly interlocked packing. `packing_aspect_ratio_of_centers_bbox` can detect if solutions are becoming overly elongated or clustered in one part of the square, which might leave empty spaces in others.
163
+ * **Spatial Uniformity**: `avg_quadrant_radii_std_dev` can highlight imbalances in radii distribution across different parts of the square. A high value might indicate that the packing is not uniform, potentially leading to localized inefficiencies. Similarly, `avg_distance_from_packing_centroid_normalized` helps understand if the packing is centrally clustered or more spread out.
164
+
165
+ These metrics collectively help diagnose whether the current high primary scores are due to truly efficient, well-distributed packing or perhaps just a clever arrangement of a few large circles that might still have room for refinement in terms of overall structural integrity and uniform space utilization. If progress on the primary metric plateaus, these auxiliary metrics can point to specific areas for improvement (e.g., better boundary contact, more uniform radii distribution, or more circles making contact with each other). This can guide future evolutionary steps toward more robust and optimal solutions.
166
+
167
+ **Spatial Arrangement Metrics:**
168
+ * **`avg_nearest_neighbor_distance_centers`**: This metric calculates the average Euclidean distance from each circle's center to the center of its closest neighboring circle. A lower value indicates a more tightly packed or clustered arrangement of circle centers, while a higher value suggests a more dispersed or sparse distribution. This is useful for identifying if the packing strategy is creating dense clusters or leaving significant gaps between groups of circles.
169
+ * **`center_quadrant_density_variance`**: This metric divides the unit square into four equal quadrants (2x2 grid) and computes the variance of the number of circle centers found within each quadrant. A lower variance indicates a more uniform distribution of circle centers across the entire packing area, suggesting a balanced utilization of space. A higher variance implies that circle centers are concentrated in certain quadrants, potentially indicating inefficient or uneven packing.
170
+
171
+
172
+ ## Generation 62 Evaluation
173
+
174
+ **Auxiliary Metrics Implemented and Refined:**
175
+
176
+ **Group 1: Packing Efficiency & Structural Metrics**
177
+ * **`empty_space_ratio`**: Represents the proportion of the unit square's area not covered by circles. This directly indicates the amount of "wasted" space. A consistently decreasing `empty_space_ratio` would indicate improving packing efficiency beyond just the sum of radii.
178
+ * **`packing_aspect_ratio_of_centers_bbox`**: This calculates the aspect ratio of the bounding box that encloses all circle centers. It helps determine if the distribution of circles is becoming more square-like (aspect ratio closer to 1) or more elongated, which could indicate a preferred packing orientation. This is useful for detecting structural biases.
179
+ * **`avg_num_touching_neighbors`**: The average number of circles that each circle is touching (within a small tolerance). A higher value suggests a more interconnected and tightly packed arrangement, which is usually a characteristic of efficient packing. This can act as a proxy for the 'connectivity' of the packing.
180
+ * **`avg_quadrant_radii_std_dev`**: The average of the standard deviations of radii within each of the four quadrants. This helps reveal if the diversity in circle sizes is localized to certain areas or if it's generally consistent across the entire packing. High values in specific quadrants might point to localized strategies for filling space.
181
+ * **`avg_pairwise_center_distance`**: The average Euclidean distance between all unique pairs of circle centers. A higher value suggests a more spread-out arrangement of circles, potentially indicating a less "clustered" or more uniform distribution of centers. This complements nearest-neighbor distance by considering all pairs and provides a global measure of center dispersion.
182
+
183
+ **Observations & Recommendations:**
184
+ With generation 83, the evolution process is likely in the "Optimization" or "Convergence" stage. The existing metrics cover basic packing efficiency, radii statistics, boundary proximity, and local spatial distribution. The newly added `avg_distance_from_unit_center_normalized` and `avg_pairwise_center_distance` will provide:
185
+ 1. **Insight into Centralization vs. Edge-utilization**: By normalizing the average distance from the center, we can better track if the solutions are becoming more or less centrally focused, offering a nuanced view beyond just boundary violations.
186
+ 2. **Global Dispersion of Centers**: The average pairwise distance offers a macro-level understanding of how spread out the centers are, which can reveal patterns like grid-like structures versus more irregular, clustered arrangements. This is crucial for understanding the overall spatial strategy of the packing, which the primary metric (sum of radii) might not fully capture.
187
+ These metrics can help identify if the evolution is settling into specific positional patterns and offer guidance on encouraging more diverse exploration if stagnation occurs.
188
+
189
+
190
+ ## Generation 103 Evaluation
191
+
192
+ **Auxiliary Metrics Implemented:**
193
+
194
+ **Group 1: Packing Efficiency**
195
+ * **`total_area_covered`**: The sum of the areas of all 26 circles (π * r^2 for each). This measures the total physical space occupied by the circles within the unit square. A higher value indicates better space utilization, offering a direct view of how much of the container is 'filled'. This metric complements the primary `sum(radii)` by considering the squared impact of radius on area.
196
+
197
+ **Group 2: Radii Statistics**
198
+ * **`avg_radius`**: The average radius of the 26 circles. This provides a central tendency for circle sizes, helping to understand if the solution favors larger, smaller, or uniformly sized circles.
199
+ * **`std_dev_radius`**: The standard deviation of the radii. This measures the dispersion or diversity in circle sizes. A high standard deviation suggests a mix of very large and very small circles, potentially indicating a strategy to fill irregular spaces. A low standard deviation implies more uniform circle sizes.
200
+ * **`min_radius`**: The smallest radius among the 26 circles. Useful for identifying if very small circles are being used to fill tiny gaps.
201
+ * **`max_radius`**: The largest radius among the 26 circles. Indicates the largest circle size successfully placed, which is often a key factor in maximizing total radius sum.
202
+
203
+ **Group 3: Boundary Proximity**
204
+ * **`avg_min_boundary_distance`**: The average of the minimum distances from the edge of each circle to the nearest boundary of the unit square. Smaller (closer to zero) values indicate that circles are packed more tightly against the boundaries, suggesting efficient utilization of the container's edges. This helps assess how well the solution leverages the container's perimeter, which is crucial for optimal packing.
205
+
206
+ **Observations and Recommendations for Generation 103:**
207
+
208
+ At Generation 103, the evolution is deep into the **Optimization/Convergence** stage. The primary score (sum of radii) might be showing diminishing returns. These auxiliary metrics are designed to provide a multi-faceted view of the packing structure:
209
+
210
+ * **`total_area_covered`**: By tracking this, we can see if increases in `sum(radii)` are also leading to proportional increases in actual area covered. If `sum(radii)` plateaus but `total_area_covered` continues to rise (or maintains a high level), it suggests a more efficient use of space by re-distributing radii or centers to fill gaps more effectively.
211
+ * **`std_dev_radius`**: This metric is critical for identifying potential local optima. If `std_dev_radius` is very low and `sum(radii)` is stagnating, it might suggest the algorithm is stuck with uniform-sized circles. Encouraging greater `std_dev_radius` could lead to exploring more diverse solutions (e.g., using a few large circles and many small ones) that might break current plateaus. Conversely, if `std_dev_radius` is high, it shows exploration of diverse sizes is still active.
212
+ * **`avg_min_boundary_distance`**: As solutions converge, an efficient packing should optimally utilize the boundaries. A consistently low (approaching zero) `avg_min_boundary_distance` combined with a high primary score indicates a well-optimized solution that is effectively using the edges of the unit square. If this value is still relatively high, it might suggest opportunities to push circles closer to the container's perimeter, thereby creating more space internally for larger circles or more circles.
213
+
214
+ These metrics, especially in conjunction with historical data, can help diagnose whether the evolution is making structural improvements that aren't immediately reflected in the primary score, or if it's truly stagnating and needs a new evolutionary pressure or strategy.
215
+
216
+ ## Generation 124 Evaluation
217
+
218
+ **Auxiliary Metrics Implemented:**
219
+
220
+ **Group 7: Spatial Distribution and Boundary Contact**
221
+ * **`avg_distance_from_packing_centroid_normalized`**: Measures how concentrated or dispersed the circle centers are. It calculates the average distance of each circle center from the overall centroid of all circle centers, normalized by the maximum possible distance a center could be from the unit square's center (0.5, 0.5) to a corner (0,0). A lower value indicates a more centrally focused and compact packing.
222
+ * **`num_circles_touching_edge`**: Counts the number of circles whose edges are "touching" (within a small tolerance of 1e-6) any of the four boundaries of the unit square. A higher count indicates more aggressive utilization of the available space by pushing circles against the container walls.
223
+ * **`num_circles_touching_corner`**: Counts the number of circles whose edges are "touching" (within a small tolerance of 1e-6) two perpendicular boundaries (i.e., a corner) of the unit square. _A more specific measure of boundary utilization, indicating how well corners are filled._
224
+
225
+ **Observations and Recommendations for Generation 124:**
226
+
227
+ At Generation 124, the evolution process is likely in an **OPTIMIZATION** or **CONVERGENCE** stage, where solutions are generally valid and the focus is on performance gains. The primary metric (`reported_sum_of_radii`) guides towards maximizing total radius.
228
+
229
+ These new metrics provide insights into *how* the packing is achieved:
230
+ * `avg_distance_from_packing_centroid_normalized`: Can help identify if solutions are becoming more centrally organized or if they are spreading out. For circle packing, a more compact central arrangement might be beneficial. If this metric shows a trend (e.g., decreasing), it suggests a more centralized packing approach.
231
+ * `num_circles_touching_edge` and `num_circles_touching_corner`: Are crucial for understanding boundary utilization. Optimal packings often involve many circles touching the boundaries. If these numbers are low, it might indicate sub-optimal use of space near the edges, and the evolution could benefit from strategies that encourage boundary contacts. Tracking the trend of these metrics can show if solutions are learning to "grip" the container more effectively.
232
+
233
+ These metrics complement the primary score by shedding light on the structural properties of the packing, which can be critical for escaping local optima or guiding further refinement.
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/eval_agent_memory/auxiliary_metrics.py ADDED
@@ -0,0 +1,477 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import numpy as np
3
+ import json
4
+ import math
5
+ from typing import Dict, Any
6
+
7
+ def evaluate_aux(results_dir: str) -> Dict[str, Any]:
8
+ """
9
+ Main entry point for all auxiliary metrics.
10
+ This function will be automatically called by the evaluation service.
11
+ """
12
+ metrics = {}
13
+ try:
14
+ data = _load_generation_data(results_dir)
15
+ if data is None or data["num_circles"] == 0:
16
+ # Return a dict with all metrics indicating data not found or no circles
17
+ # Initialize all possible metrics to 0 or appropriate default
18
+ # This ensures a flat dict is always returned, even on error or empty data
19
+ return {
20
+ "avg_radius": 0.0,
21
+ "std_dev_radius": 0.0,
22
+ "min_radius": 0.0,
23
+ "max_radius": 0.0,
24
+ "median_radius": 0.0,
25
+ "num_unique_radii": 0,
26
+ "total_area_covered": 0.0,
27
+ "packing_density": 0.0,
28
+ "empty_space_ratio": 1.0,
29
+ "max_circle_overlap_magnitude": 0.0,
30
+ "num_overlapping_pairs": 0,
31
+ "max_boundary_violation_magnitude": 0.0,
32
+ "num_boundary_violations": 0,
33
+ "packing_aspect_ratio_of_centers_bbox": 1.0,
34
+ "avg_num_touching_neighbors": 0.0,
35
+ "avg_quadrant_radii_std_dev": 0.0,
36
+ "avg_pairwise_center_distance": 0.0,
37
+ "avg_distance_from_packing_centroid_normalized": 0.0,
38
+ "num_circles_touching_edge": 0,
39
+ "num_circles_touching_corner": 0,
40
+ "avg_min_circle_to_boundary_distance": 0.0,
41
+ "error": "Failed to load generation data (extra.npz not found or corrupted) or no circles present."
42
+ }
43
+
44
+ # Aggregate all metrics in one place
45
+ metrics = {}
46
+
47
+ # Group 1: Radii Statistics
48
+ metrics.update(_calculate_radii_stats(data))
49
+
50
+ # Group 2: Area Coverage
51
+ metrics.update(_calculate_area_metrics(data))
52
+
53
+ # Group 3: Violation Metrics (useful even if primary says "correct" to see closeness to boundary)
54
+ metrics.update(_calculate_violation_metrics(data))
55
+
56
+ # Group 4: Advanced Spatial and Contact Metrics
57
+ metrics.update(_calculate_advanced_spatial_and_contact_metrics(data))
58
+
59
+ # Group 5: Pairwise Center Distances
60
+ metrics.update(_calculate_pairwise_center_distances(data))
61
+
62
+ # Group 6: Center Distribution Metrics
63
+ metrics.update(_calculate_center_distribution_metrics(data))
64
+
65
+ # Group 7: Boundary Contact Metrics
66
+ metrics.update(_calculate_boundary_contact_metrics(data))
67
+
68
+ # Group 8: Boundary Proximity (custom implementation)
69
+ try:
70
+ metrics.update(_calculate_boundary_proximity(data))
71
+ except Exception as e:
72
+ metrics["boundary_proximity_error"] = str(e)
73
+
74
+ except Exception as e:
75
+ metrics["evaluation_error"] = str(e)
76
+ metrics["failed_at"] = "evaluate_aux"
77
+
78
+ return metrics
79
+
80
+ # --- Helper functions ---
81
+
82
+ def _load_generation_data(results_dir: str) -> Dict[str, Any] | None:
83
+ """
84
+ Load common data files (e.g., extra.npz, metrics.json).
85
+ Handles cases where data might be missing or empty gracefully.
86
+ """
87
+ extra_npz_path = os.path.join(results_dir, "extra.npz")
88
+ metrics_json_path = os.path.join(results_dir, "metrics.json")
89
+
90
+ centers = np.array([])
91
+ radii = np.array([])
92
+ reported_sum = 0.0
93
+
94
+ if os.path.exists(extra_npz_path):
95
+ try:
96
+ with np.load(extra_npz_path) as data_npz:
97
+ centers = data_npz["centers"]
98
+ radii = data_npz["radii"]
99
+ reported_sum = data_npz.get("reported_sum", 0.0) # Use .get for robustness
100
+ except Exception as e:
101
+ print(f"Error loading extra.npz: {e}")
102
+
103
+ primary_score = None
104
+ if os.path.exists(metrics_json_path):
105
+ try:
106
+ with open(metrics_json_path, 'r') as f:
107
+ metrics_data = json.load(f)
108
+ primary_score = metrics_data.get("combined_score")
109
+ except Exception as e:
110
+ print(f"Error loading metrics.json: {e}")
111
+
112
+ # Always return a dict, even if no circles or data is missing.
113
+ # This allows downstream functions to handle empty arrays without immediately failing.
114
+ num_circles = len(radii) if radii.size > 0 else 0
115
+
116
+ return {
117
+ "centers": centers,
118
+ "radii": radii,
119
+ "reported_sum": reported_sum,
120
+ "primary_score": primary_score,
121
+ "num_circles": num_circles
122
+ }
123
+
124
+ def _calculate_boundary_proximity(data: Dict[str, Any]) -> Dict[str, Any]:
125
+ """
126
+ Calculates metrics related to how close circles are to the boundaries of the unit square.
127
+ - `num_circles_touching_boundary`: Count of circles whose edge is within epsilon of a boundary.
128
+ - `avg_min_distance_to_boundary`: Average of the minimum distance from each circle's edge to any boundary.
129
+ """
130
+ metrics = {}
131
+ try:
132
+ centers = data["centers"]
133
+ radii = data["radii"]
134
+ n_circles = data["num_circles"]
135
+
136
+ epsilon = 1e-4 # Threshold for considering a circle 'touching' the boundary
137
+
138
+ if n_circles == 0:
139
+ metrics["num_circles_touching_boundary"] = 0
140
+ metrics["avg_min_distance_to_boundary"] = 1.0 # Max possible distance (assuming unit square)
141
+ return metrics
142
+
143
+ touching_count = 0
144
+ min_distances_to_boundary = []
145
+
146
+ for i in range(n_circles):
147
+ x, y = centers[i]
148
+ r = radii[i]
149
+
150
+ # Distances from circle edge to boundaries
151
+ dist_left = x - r
152
+ dist_right = 1 - (x + r)
153
+ dist_bottom = y - r
154
+ dist_top = 1 - (y + r)
155
+
156
+ current_min_dist = min(dist_left, dist_right, dist_bottom, dist_top)
157
+ min_distances_to_boundary.append(current_min_dist)
158
+
159
+ if current_min_dist <= epsilon:
160
+ touching_count += 1
161
+
162
+ metrics["num_circles_touching_boundary"] = touching_count
163
+ metrics["avg_min_distance_to_boundary"] = float(np.mean(min_distances_to_boundary)) if min_distances_to_boundary else 1.0
164
+
165
+ except Exception as e:
166
+ metrics["boundary_proximity_error"] = str(e)
167
+ return metrics
168
+
169
+ def _calculate_radii_stats(data: Dict[str, Any]) -> Dict[str, float]:
170
+ """Calculate basic statistical metrics for circle radii."""
171
+ metrics = {}
172
+ try:
173
+ radii = data["radii"]
174
+ if radii.size > 0:
175
+ metrics["avg_radius"] = float(np.mean(radii))
176
+ metrics["std_dev_radius"] = float(np.std(radii))
177
+ metrics["min_radius"] = float(np.min(radii))
178
+ metrics["max_radius"] = float(np.max(radii))
179
+ metrics["median_radius"] = float(np.median(radii))
180
+ metrics["num_unique_radii"] = int(len(np.unique(radii)))
181
+ if metrics["avg_radius"] > 0:
182
+ metrics["radius_coefficient_of_variation"] = float(metrics["std_dev_radius"] / metrics["avg_radius"])
183
+ else:
184
+ metrics["radius_coefficient_of_variation"] = 0.0
185
+ else:
186
+ metrics["avg_radius"] = 0.0
187
+ metrics["std_dev_radius"] = 0.0
188
+ metrics["min_radius"] = 0.0
189
+ metrics["max_radius"] = 0.0
190
+ metrics["median_radius"] = 0.0
191
+ metrics["num_unique_radii"] = 0
192
+ metrics["radius_coefficient_of_variation"] = 0.0
193
+ metrics["radii_stats_info"] = "No radii data available for stats."
194
+ except Exception as e:
195
+ metrics["radii_stats_error"] = str(e)
196
+ return metrics
197
+
198
+ def _calculate_area_metrics(data: Dict[str, Any]) -> Dict[str, float]:
199
+ """Calculate metrics related to the total area covered by circles."""
200
+ metrics = {}
201
+ try:
202
+ radii = data["radii"]
203
+ if radii.size > 0:
204
+ total_circle_area = np.sum(math.pi * (radii ** 2))
205
+ unit_square_area = 1.0 # Unit square is [0,1]x[0,1]
206
+
207
+ metrics["total_area_covered"] = float(total_circle_area)
208
+ metrics["packing_density"] = float(total_circle_area / unit_square_area)
209
+ metrics["empty_space_ratio"] = float(1.0 - metrics["packing_density"])
210
+
211
+ else:
212
+ metrics["total_area_covered"] = 0.0
213
+ metrics["packing_density"] = 0.0
214
+ metrics["empty_space_ratio"] = 1.0
215
+ metrics["area_metrics_info"] = "No radii data available to calculate area metrics."
216
+ except Exception as e:
217
+ metrics["area_metrics_error"] = str(e)
218
+ return metrics
219
+
220
+ def _calculate_violation_metrics(data: Dict[str, Any]) -> Dict[str, Any]:
221
+ """Calculate metrics related to overlaps and boundary violations.
222
+ These are typically boolean for passing primary eval, but magnitudes give insight.
223
+ """
224
+ metrics = {}
225
+ try:
226
+ centers = data["centers"]
227
+ radii = data["radii"]
228
+ n_circles = data["num_circles"]
229
+
230
+ if n_circles == 0:
231
+ metrics["max_circle_overlap_magnitude"] = 0.0
232
+ metrics["num_overlapping_pairs"] = 0
233
+ metrics["max_boundary_violation_magnitude"] = 0.0
234
+ metrics["num_boundary_violations"] = 0
235
+ metrics["violation_metrics_info"] = "No circles to check for violations."
236
+ return metrics
237
+
238
+ max_circle_overlap_magnitude = 0.0
239
+ num_overlapping_pairs = 0
240
+
241
+ # Circle-circle overlaps
242
+ for i in range(n_circles):
243
+ for j in range(i + 1, n_circles):
244
+ dist = np.linalg.norm(centers[i] - centers[j])
245
+ overlap = radii[i] + radii[j] - dist
246
+ if overlap > 0: # An overlap is a violation
247
+ num_overlapping_pairs += 1
248
+ if overlap > max_circle_overlap_magnitude:
249
+ max_circle_overlap_magnitude = overlap
250
+
251
+ metrics["max_circle_overlap_magnitude"] = float(max_circle_overlap_magnitude)
252
+ metrics["num_overlapping_pairs"] = num_overlapping_pairs
253
+
254
+ max_boundary_violation_magnitude = 0.0
255
+ num_boundary_violations = 0
256
+
257
+ # Boundary violations
258
+ for i in range(n_circles):
259
+ x, y = centers[i]
260
+ r = radii[i]
261
+
262
+ # Calculate potential violations for each boundary
263
+ violations_per_circle = [
264
+ r - x, # Left boundary (x - r < 0 => r - x > 0)
265
+ x + r - 1.0, # Right boundary (x + r > 1 => x + r - 1 > 0)
266
+ r - y, # Bottom boundary (y - r < 0 => r - y > 0)
267
+ y + r - 1.0, # Top boundary (y + r > 1 => y + r - 1 > 0)
268
+ ]
269
+
270
+ max_violation_for_circle = 0.0
271
+ for v in violations_per_circle:
272
+ if v > 1e-6: # Consider a violation if it's significantly positive
273
+ num_boundary_violations += 1
274
+ if v > max_violation_for_circle:
275
+ max_violation_for_circle = v
276
+
277
+ if max_violation_for_circle > max_boundary_violation_magnitude:
278
+ max_boundary_violation_magnitude = max_violation_for_circle
279
+
280
+ metrics["max_boundary_violation_magnitude"] = float(max_boundary_violation_magnitude)
281
+ metrics["num_boundary_violations"] = num_boundary_violations
282
+
283
+ except Exception as e:
284
+ metrics["violation_metrics_error"] = str(e)
285
+ return metrics
286
+
287
+ def _calculate_advanced_spatial_and_contact_metrics(data: Dict[str, Any]) -> Dict[str, Any]:
288
+ """
289
+ Calculate advanced spatial distribution metrics:
290
+ 1. Aspect Ratio of Bounding Box of Circle Centers
291
+ 2. Average Number of Touching Neighbors
292
+ 3. Variance of Radii per Quadrant
293
+ """
294
+ metrics = {}
295
+ try:
296
+ centers = data["centers"]
297
+ radii = data["radii"]
298
+ n_circles = data["num_circles"]
299
+
300
+ if n_circles == 0:
301
+ metrics["packing_aspect_ratio_of_centers_bbox"] = 1.0
302
+ metrics["avg_num_touching_neighbors"] = 0.0
303
+ metrics["avg_quadrant_radii_std_dev"] = 0.0
304
+ metrics["advanced_spatial_and_contact_metrics_info"] = "No circles for advanced spatial metrics."
305
+ return metrics
306
+
307
+ # Metric 1: Aspect Ratio of Bounding Box of Circle Centers
308
+ if n_circles > 1:
309
+ min_x, max_x = np.min(centers[:, 0]), np.max(centers[:, 0])
310
+ min_y, max_y = np.min(centers[:, 1]), np.max(centers[:, 1])
311
+
312
+ width = max_x - min_x
313
+ height = max_y - min_y
314
+
315
+ # Avoid division by zero if all centers are on a line
316
+ if width > 1e-6 and height > 1e-6:
317
+ aspect_ratio = max(width, height) / min(width, height)
318
+ else: # If all points are on a line, or single point, aspect ratio is effectively infinite or 1
319
+ aspect_ratio = 1.0
320
+ metrics["packing_aspect_ratio_of_centers_bbox"] = float(aspect_ratio)
321
+ else:
322
+ metrics["packing_aspect_ratio_of_centers_bbox"] = 1.0 # Single circle, assume perfect aspect ratio
323
+
324
+ # Metric 2: Contact Count (Average Number of Touching Neighbors)
325
+ contact_counts = np.zeros(n_circles)
326
+ touch_tolerance = 1e-6
327
+
328
+ for i in range(n_circles):
329
+ for j in range(i + 1, n_circles):
330
+ dist_centers = np.linalg.norm(centers[i] - centers[j])
331
+ sum_radii = radii[i] + radii[j]
332
+
333
+ if np.isclose(dist_centers, sum_radii, atol=touch_tolerance):
334
+ contact_counts[i] += 1
335
+ contact_counts[j] += 1
336
+
337
+ metrics["avg_num_touching_neighbors"] = float(np.mean(contact_counts)) if n_circles > 0 else 0.0
338
+
339
+ # Metric 3: Variance of Radii per Quadrant
340
+ quadrant_radii = [[], [], [], []]
341
+ for i in range(n_circles):
342
+ x, y = centers[i]
343
+ r = radii[i]
344
+
345
+ if x < 0.5 and y < 0.5:
346
+ quadrant_radii[0].append(r)
347
+ elif x >= 0.5 and y < 0.5:
348
+ quadrant_radii[1].append(r)
349
+ elif x < 0.5 and y >= 0.5:
350
+ quadrant_radii[2].append(r)
351
+ elif x >= 0.5 and y >= 0.5:
352
+ quadrant_radii[3].append(r)
353
+
354
+ quadrant_radii_std_devs = [
355
+ np.std(q) for q in quadrant_radii if len(q) > 1
356
+ ] # Only calculate std dev if more than one radius in quadrant
357
+
358
+ metrics["avg_quadrant_radii_std_dev"] = float(np.mean(quadrant_radii_std_devs)) if quadrant_radii_std_devs else 0.0
359
+
360
+ except Exception as e:
361
+ metrics["advanced_spatial_and_contact_metrics_error"] = str(e)
362
+ return metrics
363
+
364
+
365
+ def _calculate_pairwise_center_distances(data: Dict[str, Any]) -> Dict[str, float]:
366
+ """Calculate the average pairwise distance between all circle centers."""
367
+ metrics = {}
368
+ try:
369
+ centers = data["centers"]
370
+ n_circles = data["num_circles"]
371
+
372
+ if n_circles < 2:
373
+ metrics["avg_pairwise_center_distance"] = 0.0
374
+ metrics["pairwise_center_distance_info"] = "Need at least 2 circles for pairwise distance."
375
+ return metrics
376
+
377
+ distances = []
378
+ for i in range(n_circles):
379
+ for j in range(i + 1, n_circles):
380
+ dist = np.linalg.norm(centers[i] - centers[j])
381
+ distances.append(dist)
382
+
383
+ if distances:
384
+ metrics["avg_pairwise_center_distance"] = float(np.mean(distances))
385
+ else:
386
+ metrics["avg_pairwise_center_distance"] = 0.0
387
+
388
+ except Exception as e:
389
+ metrics["error_pairwise_center_distance"] = str(e)
390
+ return metrics
391
+
392
+
393
+ def _calculate_center_distribution_metrics(data: Dict[str, Any]) -> Dict[str, float]:
394
+ """
395
+ Calculate metrics related to the spatial distribution and concentration of circle centers.
396
+ """
397
+ metrics = {}
398
+ try:
399
+ centers = data["centers"]
400
+ n_circles = data["num_circles"]
401
+
402
+ if n_circles == 0:
403
+ metrics["avg_distance_from_packing_centroid_normalized"] = 0.0
404
+ metrics["center_distribution_info"] = "No circles for center distribution metrics."
405
+ return metrics
406
+
407
+ # Calculate packing centroid
408
+ packing_centroid = np.mean(centers, axis=0)
409
+
410
+ # Calculate average distance from packing centroid
411
+ distances_from_centroid = np.linalg.norm(centers - packing_centroid, axis=1)
412
+ avg_dist_from_centroid = np.mean(distances_from_centroid)
413
+
414
+ # Normalize by max possible distance from unit square center (0.5, 0.5) to corner (0,0)
415
+ # Max distance from any point in unit square to (0.5, 0.5) is sqrt(0.5^2 + 0.5^2) = sqrt(0.5)
416
+ max_possible_dist_from_unit_center = math.sqrt(0.5)
417
+
418
+ metrics["avg_distance_from_packing_centroid_normalized"] = float(avg_dist_from_centroid / max_possible_dist_from_unit_center)
419
+
420
+ except Exception as e:
421
+ metrics["error_center_distribution_metrics"] = str(e)
422
+ return metrics
423
+
424
+ def _calculate_boundary_contact_metrics(data: Dict[str, Any]) -> Dict[str, int]:
425
+ """
426
+ Count how many circles are touching the edges or corners of the unit square.
427
+ """
428
+ metrics = {}
429
+ try:
430
+ centers = data["centers"]
431
+ radii = data["radii"]
432
+ n_circles = data["num_circles"]
433
+
434
+ if n_circles == 0:
435
+ metrics["num_circles_touching_edge"] = 0
436
+ metrics["num_circles_touching_corner"] = 0
437
+ metrics["boundary_contact_info"] = "No circles for boundary contact metrics."
438
+ return metrics
439
+
440
+ num_edge_contacts = 0
441
+ num_corner_contacts = 0
442
+
443
+ tolerance = 1e-6 # Based on primary evaluator's atol
444
+
445
+ for i in range(n_circles):
446
+ x, y = centers[i]
447
+ r = radii[i]
448
+
449
+ touching_left = abs(x - r) < tolerance
450
+ touching_right = abs(x + r - 1.0) < tolerance
451
+ touching_bottom = abs(y - r) < tolerance
452
+ touching_top = abs(y + r - 1.0) < tolerance
453
+
454
+ # Check for edge contact
455
+ if touching_left or touching_right or touching_bottom or touching_top:
456
+ num_edge_contacts += 1
457
+
458
+ # Check for corner contact (touching two perpendicular edges)
459
+ # This logic needs to be careful: a circle touching x=0 and x=1 means it's super wide.
460
+ # We want genuinely touching *corners*, meaning meeting two *orthogonal* boundaries.
461
+ corner_touch_count = 0
462
+ if touching_left and touching_bottom: corner_touch_count += 1
463
+ if touching_left and touching_top: corner_touch_count += 1
464
+ if touching_right and touching_bottom: corner_touch_count += 1
465
+ if touching_right and touching_top: corner_touch_count += 1
466
+
467
+ # If a circle is touching 2 or more *orthogonal* boundaries, it's considered touching a corner.
468
+ # Example: touching_left and touching_bottom means touching the bottom-left corner.
469
+ if corner_touch_count > 0:
470
+ num_corner_contacts += 1
471
+
472
+ metrics["num_circles_touching_edge"] = num_edge_contacts
473
+ metrics["num_circles_touching_corner"] = num_corner_contacts
474
+
475
+ except Exception as e:
476
+ metrics["error_boundary_contact_metrics"] = str(e)
477
+ return metrics
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/eval_agent_memory/service_state.json ADDED
@@ -0,0 +1,608 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "generation_history": [
3
+ {
4
+ "generation": 100,
5
+ "primary_score": 2.2800000000000002,
6
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_100/results",
7
+ "timestamp": 1770497536.7834485
8
+ },
9
+ {
10
+ "generation": 101,
11
+ "primary_score": 2.465,
12
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_101/results",
13
+ "timestamp": 1770497618.2956038
14
+ },
15
+ {
16
+ "generation": 102,
17
+ "primary_score": 0.0,
18
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/results",
19
+ "timestamp": 1770497647.555699
20
+ },
21
+ {
22
+ "generation": 103,
23
+ "primary_score": 2.5208244546058056,
24
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_103/results",
25
+ "timestamp": 1770497720.1201556
26
+ },
27
+ {
28
+ "generation": 104,
29
+ "primary_score": 2.405,
30
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/results",
31
+ "timestamp": 1770497811.336844
32
+ },
33
+ {
34
+ "generation": 105,
35
+ "primary_score": 2.4470613318956036,
36
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_105/results",
37
+ "timestamp": 1770497866.173539
38
+ },
39
+ {
40
+ "generation": 106,
41
+ "primary_score": 2.444,
42
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_106/results",
43
+ "timestamp": 1770497981.7272277
44
+ },
45
+ {
46
+ "generation": 107,
47
+ "primary_score": 2.4650000000000007,
48
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_107/results",
49
+ "timestamp": 1770498054.5811007
50
+ },
51
+ {
52
+ "generation": 108,
53
+ "primary_score": 2.4499999999999997,
54
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_108/results",
55
+ "timestamp": 1770498178.0888407
56
+ },
57
+ {
58
+ "generation": 109,
59
+ "primary_score": 2.5069999999999992,
60
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_109/results",
61
+ "timestamp": 1770498225.3792388
62
+ },
63
+ {
64
+ "generation": 110,
65
+ "primary_score": 2.5066514305478123,
66
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_110/results",
67
+ "timestamp": 1770498377.175865
68
+ },
69
+ {
70
+ "generation": 111,
71
+ "primary_score": 2.509079058224618,
72
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_111/results",
73
+ "timestamp": 1770498432.0109513
74
+ },
75
+ {
76
+ "generation": 112,
77
+ "primary_score": 2.44,
78
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_112/results",
79
+ "timestamp": 1770498535.7547326
80
+ },
81
+ {
82
+ "generation": 113,
83
+ "primary_score": 2.522059081864002,
84
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_113/results",
85
+ "timestamp": 1770498593.591211
86
+ },
87
+ {
88
+ "generation": 114,
89
+ "primary_score": 2.477987673473837,
90
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_114/results",
91
+ "timestamp": 1770498820.0450184
92
+ },
93
+ {
94
+ "generation": 115,
95
+ "primary_score": 2.5069999999999997,
96
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_115/results",
97
+ "timestamp": 1770498872.2368124
98
+ },
99
+ {
100
+ "generation": 117,
101
+ "primary_score": 2.5071705429430855,
102
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_117/results",
103
+ "timestamp": 1770498950.9931102
104
+ },
105
+ {
106
+ "generation": 116,
107
+ "primary_score": 0.0,
108
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_116/results",
109
+ "timestamp": 1770499045.9516294
110
+ },
111
+ {
112
+ "generation": 118,
113
+ "primary_score": 2.5069999999999997,
114
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_118/results",
115
+ "timestamp": 1770499050.147209
116
+ },
117
+ {
118
+ "generation": 119,
119
+ "primary_score": 2.5069999999999997,
120
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_119/results",
121
+ "timestamp": 1770499090.2079575
122
+ },
123
+ {
124
+ "generation": 120,
125
+ "primary_score": 2.5072159305410415,
126
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_120/results",
127
+ "timestamp": 1770499238.8178396
128
+ },
129
+ {
130
+ "generation": 121,
131
+ "primary_score": 2.5218608001632576,
132
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_121/results",
133
+ "timestamp": 1770499311.4899046
134
+ },
135
+ {
136
+ "generation": 122,
137
+ "primary_score": 2.5199999999999996,
138
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_122/results",
139
+ "timestamp": 1770499415.1481774
140
+ },
141
+ {
142
+ "generation": 123,
143
+ "primary_score": 2.2851216373415686,
144
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_123/results",
145
+ "timestamp": 1770499524.1350317
146
+ },
147
+ {
148
+ "generation": 124,
149
+ "primary_score": 2.5208244546058047,
150
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_124/results",
151
+ "timestamp": 1770499555.8882217
152
+ },
153
+ {
154
+ "generation": 125,
155
+ "primary_score": 2.4650280623580625,
156
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_125/results",
157
+ "timestamp": 1770499628.8779595
158
+ },
159
+ {
160
+ "generation": 126,
161
+ "primary_score": 2.5218608001632576,
162
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_126/results",
163
+ "timestamp": 1770499689.4132166
164
+ },
165
+ {
166
+ "generation": 127,
167
+ "primary_score": 2.4290000000000003,
168
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_127/results",
169
+ "timestamp": 1770499822.837822
170
+ },
171
+ {
172
+ "generation": 128,
173
+ "primary_score": 2.4227558888552587,
174
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_128/results",
175
+ "timestamp": 1770499895.2269728
176
+ },
177
+ {
178
+ "generation": 129,
179
+ "primary_score": 2.5208244546058056,
180
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_129/results",
181
+ "timestamp": 1770499934.2850866
182
+ },
183
+ {
184
+ "generation": 130,
185
+ "primary_score": 2.5167649962323697,
186
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_130/results",
187
+ "timestamp": 1770500005.944343
188
+ },
189
+ {
190
+ "generation": 131,
191
+ "primary_score": 2.5208244546058056,
192
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_131/results",
193
+ "timestamp": 1770500173.9580781
194
+ },
195
+ {
196
+ "generation": 132,
197
+ "primary_score": 2.5145014798557748,
198
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_132/results",
199
+ "timestamp": 1770500225.3639982
200
+ },
201
+ {
202
+ "generation": 133,
203
+ "primary_score": 2.4977248512193455,
204
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_133/results",
205
+ "timestamp": 1770500293.4483747
206
+ },
207
+ {
208
+ "generation": 134,
209
+ "primary_score": 2.3684442106867847,
210
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_134/results",
211
+ "timestamp": 1770500380.2947817
212
+ },
213
+ {
214
+ "generation": 135,
215
+ "primary_score": 2.521567718144885,
216
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_135/results",
217
+ "timestamp": 1770500431.7316802
218
+ },
219
+ {
220
+ "generation": 136,
221
+ "primary_score": 2.5169176564937668,
222
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_136/results",
223
+ "timestamp": 1770500479.809213
224
+ },
225
+ {
226
+ "generation": 137,
227
+ "primary_score": 2.521860800163257,
228
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_137/results",
229
+ "timestamp": 1770500545.8810725
230
+ },
231
+ {
232
+ "generation": 138,
233
+ "primary_score": 2.5167649962323697,
234
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_138/results",
235
+ "timestamp": 1770500613.068937
236
+ },
237
+ {
238
+ "generation": 139,
239
+ "primary_score": 2.5208244546058056,
240
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_139/results",
241
+ "timestamp": 1770500662.9420853
242
+ },
243
+ {
244
+ "generation": 140,
245
+ "primary_score": 2.5152982477911427,
246
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_140/results",
247
+ "timestamp": 1770500806.6727757
248
+ },
249
+ {
250
+ "generation": 141,
251
+ "primary_score": 2.489,
252
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_141/results",
253
+ "timestamp": 1770500888.205382
254
+ },
255
+ {
256
+ "generation": 142,
257
+ "primary_score": 2.5208244546058056,
258
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_142/results",
259
+ "timestamp": 1770500943.419112
260
+ },
261
+ {
262
+ "generation": 143,
263
+ "primary_score": 2.500028148484375,
264
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_143/results",
265
+ "timestamp": 1770500972.1809523
266
+ },
267
+ {
268
+ "generation": 144,
269
+ "primary_score": 2.5220590818640027,
270
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_144/results",
271
+ "timestamp": 1770501035.3806226
272
+ },
273
+ {
274
+ "generation": 145,
275
+ "primary_score": 2.3564139033400773,
276
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_145/results",
277
+ "timestamp": 1770501113.6389701
278
+ },
279
+ {
280
+ "generation": 146,
281
+ "primary_score": 2.5010000000000003,
282
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_146/results",
283
+ "timestamp": 1770501201.063461
284
+ },
285
+ {
286
+ "generation": 147,
287
+ "primary_score": 2.5121925450980367,
288
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_147/results",
289
+ "timestamp": 1770501356.2212741
290
+ },
291
+ {
292
+ "generation": 148,
293
+ "primary_score": 2.465028062358063,
294
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_148/results",
295
+ "timestamp": 1770501480.849577
296
+ },
297
+ {
298
+ "generation": 149,
299
+ "primary_score": 2.494020111763647,
300
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_149/results",
301
+ "timestamp": 1770501562.2326796
302
+ },
303
+ {
304
+ "generation": 150,
305
+ "primary_score": 2.5166368302058504,
306
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_150/results",
307
+ "timestamp": 1770501643.7143664
308
+ },
309
+ {
310
+ "generation": 151,
311
+ "primary_score": 2.3152000000000004,
312
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_151/results",
313
+ "timestamp": 1770501729.8692663
314
+ },
315
+ {
316
+ "generation": 152,
317
+ "primary_score": 2.444327408971052,
318
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_152/results",
319
+ "timestamp": 1770501799.3386772
320
+ },
321
+ {
322
+ "generation": 153,
323
+ "primary_score": 2.400601531744524,
324
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_153/results",
325
+ "timestamp": 1770501889.992735
326
+ },
327
+ {
328
+ "generation": 154,
329
+ "primary_score": 2.464061310932331,
330
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_154/results",
331
+ "timestamp": 1770501975.5777493
332
+ },
333
+ {
334
+ "generation": 155,
335
+ "primary_score": 1.722,
336
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_155/results",
337
+ "timestamp": 1770502121.2160032
338
+ },
339
+ {
340
+ "generation": 156,
341
+ "primary_score": 2.5000330771530224,
342
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_156/results",
343
+ "timestamp": 1770502146.0164196
344
+ },
345
+ {
346
+ "generation": 157,
347
+ "primary_score": 2.5158597847082476,
348
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_157/results",
349
+ "timestamp": 1770502206.5066895
350
+ },
351
+ {
352
+ "generation": 158,
353
+ "primary_score": 2.5187310369370173,
354
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_158/results",
355
+ "timestamp": 1770502252.9060795
356
+ },
357
+ {
358
+ "generation": 159,
359
+ "primary_score": 2.405,
360
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_159/results",
361
+ "timestamp": 1770502338.09743
362
+ },
363
+ {
364
+ "generation": 160,
365
+ "primary_score": 2.507448154084946,
366
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_160/results",
367
+ "timestamp": 1770502427.1638386
368
+ },
369
+ {
370
+ "generation": 161,
371
+ "primary_score": 2.45729952347023,
372
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_161/results",
373
+ "timestamp": 1770502464.0104778
374
+ },
375
+ {
376
+ "generation": 162,
377
+ "primary_score": 2.355028062358063,
378
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_162/results",
379
+ "timestamp": 1770502548.8020644
380
+ },
381
+ {
382
+ "generation": 163,
383
+ "primary_score": 2.3906342324181162,
384
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_163/results",
385
+ "timestamp": 1770502653.7126782
386
+ },
387
+ {
388
+ "generation": 164,
389
+ "primary_score": 2.375,
390
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_164/results",
391
+ "timestamp": 1770502742.5551453
392
+ },
393
+ {
394
+ "generation": 165,
395
+ "primary_score": 2.4650280623580625,
396
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_165/results",
397
+ "timestamp": 1770502837.1902678
398
+ },
399
+ {
400
+ "generation": 166,
401
+ "primary_score": 2.4974535092172165,
402
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_166/results",
403
+ "timestamp": 1770502974.159942
404
+ },
405
+ {
406
+ "generation": 167,
407
+ "primary_score": 2.5221857096486984,
408
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_167/results",
409
+ "timestamp": 1770503032.474351
410
+ },
411
+ {
412
+ "generation": 168,
413
+ "primary_score": 2.5208244546058056,
414
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_168/results",
415
+ "timestamp": 1770503122.8883655
416
+ },
417
+ {
418
+ "generation": 169,
419
+ "primary_score": 2.408028735854656,
420
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_169/results",
421
+ "timestamp": 1770503217.3845317
422
+ },
423
+ {
424
+ "generation": 170,
425
+ "primary_score": 2.4659999999999997,
426
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_170/results",
427
+ "timestamp": 1770503361.6872094
428
+ },
429
+ {
430
+ "generation": 171,
431
+ "primary_score": 2.458419304336672,
432
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_171/results",
433
+ "timestamp": 1770503450.1529338
434
+ },
435
+ {
436
+ "generation": 172,
437
+ "primary_score": 2.410029184852385,
438
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_172/results",
439
+ "timestamp": 1770503545.988076
440
+ },
441
+ {
442
+ "generation": 173,
443
+ "primary_score": 2.3770631504141666,
444
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_173/results",
445
+ "timestamp": 1770503617.5602458
446
+ },
447
+ {
448
+ "generation": 174,
449
+ "primary_score": 2.5208244546058056,
450
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_174/results",
451
+ "timestamp": 1770503659.8928645
452
+ },
453
+ {
454
+ "generation": 175,
455
+ "primary_score": 2.499976257191056,
456
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_175/results",
457
+ "timestamp": 1770503740.7310476
458
+ },
459
+ {
460
+ "generation": 176,
461
+ "primary_score": 2.47954789853076,
462
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_176/results",
463
+ "timestamp": 1770503796.8694088
464
+ },
465
+ {
466
+ "generation": 177,
467
+ "primary_score": 2.4650280623580625,
468
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_177/results",
469
+ "timestamp": 1770503854.1147568
470
+ },
471
+ {
472
+ "generation": 178,
473
+ "primary_score": 2.5373504944627845,
474
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_178/results",
475
+ "timestamp": 1770503975.0813587
476
+ },
477
+ {
478
+ "generation": 179,
479
+ "primary_score": 2.4710280623580627,
480
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_179/results",
481
+ "timestamp": 1770504019.5344222
482
+ },
483
+ {
484
+ "generation": 180,
485
+ "primary_score": 2.5220087596017353,
486
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_180/results",
487
+ "timestamp": 1770504154.3909054
488
+ },
489
+ {
490
+ "generation": 181,
491
+ "primary_score": 2.5166368302058513,
492
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_181/results",
493
+ "timestamp": 1770504174.088877
494
+ },
495
+ {
496
+ "generation": 182,
497
+ "primary_score": 2.5049983209898663,
498
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_182/results",
499
+ "timestamp": 1770504222.2504957
500
+ },
501
+ {
502
+ "generation": 184,
503
+ "primary_score": 2.5121925450980367,
504
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_184/results",
505
+ "timestamp": 1770504302.5232344
506
+ },
507
+ {
508
+ "generation": 183,
509
+ "primary_score": 2.5462385637519325,
510
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_183/results",
511
+ "timestamp": 1770504326.7040734
512
+ },
513
+ {
514
+ "generation": 185,
515
+ "primary_score": 2.5166368302058513,
516
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_185/results",
517
+ "timestamp": 1770504388.9125996
518
+ },
519
+ {
520
+ "generation": 186,
521
+ "primary_score": 2.5166368302058513,
522
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_186/results",
523
+ "timestamp": 1770504424.4280624
524
+ },
525
+ {
526
+ "generation": 187,
527
+ "primary_score": 2.405,
528
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_187/results",
529
+ "timestamp": 1770504481.1124032
530
+ },
531
+ {
532
+ "generation": 189,
533
+ "primary_score": 2.5208244546058056,
534
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_189/results",
535
+ "timestamp": 1770504611.9533465
536
+ },
537
+ {
538
+ "generation": 188,
539
+ "primary_score": 2.5783841891678096,
540
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_188/results",
541
+ "timestamp": 1770504612.5975885
542
+ },
543
+ {
544
+ "generation": 190,
545
+ "primary_score": 2.5166368302058504,
546
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_190/results",
547
+ "timestamp": 1770504728.5890408
548
+ },
549
+ {
550
+ "generation": 191,
551
+ "primary_score": 2.5208244546058056,
552
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_191/results",
553
+ "timestamp": 1770504741.938369
554
+ },
555
+ {
556
+ "generation": 192,
557
+ "primary_score": 2.5166368302058513,
558
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_192/results",
559
+ "timestamp": 1770504749.9977677
560
+ },
561
+ {
562
+ "generation": 194,
563
+ "primary_score": 2.5208244546058056,
564
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_194/results",
565
+ "timestamp": 1770505016.9855785
566
+ },
567
+ {
568
+ "generation": 193,
569
+ "primary_score": 2.593165588404451,
570
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_193/results",
571
+ "timestamp": 1770505036.2274702
572
+ },
573
+ {
574
+ "generation": 195,
575
+ "primary_score": 2.5929895930074904,
576
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_195/results",
577
+ "timestamp": 1770505139.6044135
578
+ },
579
+ {
580
+ "generation": 197,
581
+ "primary_score": 2.5238500413244105,
582
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_197/results",
583
+ "timestamp": 1770505252.0155315
584
+ },
585
+ {
586
+ "generation": 196,
587
+ "primary_score": 0.0,
588
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_196/results",
589
+ "timestamp": 1770505276.414447
590
+ },
591
+ {
592
+ "generation": 198,
593
+ "primary_score": 2.5238500413244105,
594
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_198/results",
595
+ "timestamp": 1770505348.782338
596
+ },
597
+ {
598
+ "generation": 199,
599
+ "primary_score": 2.5208244546058056,
600
+ "results_dir": "/home/tengxiao/pj/ShinkaEvolve/examples/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_199/results",
601
+ "timestamp": 1770505403.1935954
602
+ }
603
+ ],
604
+ "last_agent_trigger_gen": 185,
605
+ "total_notifications": 199,
606
+ "total_agent_runs": 18,
607
+ "last_update": 1770512444.365326
608
+ }
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/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/results_full_gen200_period20_20260207_182944/gen_1/edit.diff ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,94 +1,153 @@
4
+ # EVOLVE-BLOCK-START
5
+ -"""Constructor-based circle packing for n=26 circles"""
6
+ +"""
7
+ +Constructor-based circle packing for n=26 circles using a hierarchical grid
8
+ +and linear programming for optimal radii.
9
+ +"""
10
+
11
+ import numpy as np
12
+ +from scipy.optimize import linprog
13
+
14
+
15
+ def construct_packing():
16
+ """
17
+ - Construct a specific arrangement of 26 circles in a unit square
18
+ - that attempts to maximize the sum of their radii.
19
+ + Constructs a packing of 26 circles based on a hierarchical grid structure.
20
+ + The radii are then optimized using a linear programming solver to maximize their sum.
21
+
22
+ Returns:
23
+ - Tuple of (centers, radii, sum_of_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
+ + Tuple of (centers, radii)
28
+ + centers: np.array of shape (26, 2) with (x, y) coordinates.
29
+ + radii: np.array of shape (26) with radius of each circle.
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
+ + # Parameters for the hierarchical grid. These are chosen to create a
38
+ + # balanced initial configuration. R is the conceptual radius for the main
39
+ + # grid, which determines the overall scale. d splits the central gap.
40
+ + R = 0.121 # Base radius for the 4x4 grid
41
+ + d = 0.055 # Displacement for the two central circles
42
+
43
+ - # First, place a large circle in the center
44
+ - centers[0] = [0.5, 0.5]
45
+ + # Center the entire structure within the unit square
46
+ + margin = (1.0 - 8 * R) / 2.0
47
+
48
+ - # Place 8 circles around it in a ring
49
+ - for i in range(8):
50
+ - angle = 2 * np.pi * i / 8
51
+ - centers[i + 1] = [0.5 + 0.3 * np.cos(angle), 0.5 + 0.3 * np.sin(angle)]
52
+ + k = 0
53
+
54
+ - # Place 16 more circles in an outer ring
55
+ - for i in range(16):
56
+ - angle = 2 * np.pi * i / 16
57
+ - centers[i + 9] = [0.5 + 0.7 * np.cos(angle), 0.5 + 0.7 * np.sin(angle)]
58
+ + # 1. Place 16 primary circles in a 4x4 grid
59
+ + for i in range(4):
60
+ + for j in range(4):
61
+ + x = margin + (2 * i + 1) * R
62
+ + y = margin + (2 * j + 1) * R
63
+ + centers[k] = [x, y]
64
+ + k += 1
65
+
66
+ - # Additional positioning adjustment to make sure all circles
67
+ - # are inside the square and don't overlap
68
+ - # Clip to ensure everything is inside the unit square
69
+ - centers = np.clip(centers, 0.01, 0.99)
70
+ + # 2. Place 8 secondary circles in the 3x3 interstitial gaps, skipping the center
71
+ + for i in range(3):
72
+ + for j in range(3):
73
+ + if i == 1 and j == 1:
74
+ + continue
75
+ + x = margin + (2 * (i + 1)) * R
76
+ + y = margin + (2 * (j + 1)) * R
77
+ + centers[k] = [x, y]
78
+ + k += 1
79
+
80
+ - # Compute maximum valid radii for this configuration
81
+ + # 3. Place 2 tertiary circles in the central gap, split by 'd'
82
+ + center_point = margin + 4 * R
83
+ + centers[k] = [center_point, center_point - d]
84
+ + k += 1
85
+ + centers[k] = [center_point, center_point + d]
86
+ + k += 1
87
+ +
88
+ + # For the given centers, compute the radii that maximize the sum.
89
+ radii = compute_max_radii(centers)
90
+ +
91
+ return centers, radii
92
+
93
+
94
+ def compute_max_radii(centers):
95
+ """
96
+ - Compute the maximum possible radii for each circle position
97
+ - such that they don't overlap and stay within the unit square.
98
+ + Computes the maximum possible radii for a given set of circle centers
99
+ + by solving a linear programming problem. This maximizes the sum of radii
100
+ + subject to non-overlapping and boundary constraints.
101
+
102
+ Args:
103
+ - centers: np.array of shape (n, 2) with (x, y) coordinates
104
+ + centers: np.array of shape (n, 2) with (x, y) coordinates.
105
+
106
+ Returns:
107
+ - np.array of shape (n) with radius of each circle
108
+ + np.array of shape (n) with the optimal radius for each circle.
109
+ """
110
+ n = centers.shape[0]
111
+ - radii = np.ones(n)
112
+
113
+ - # First, limit by distance to square borders
114
+ + # The objective is to maximize sum(radii), which is equivalent to
115
+ + # minimizing sum(-radii).
116
+ + c = -np.ones(n)
117
+ +
118
+ + # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
119
+ + # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
120
+ + # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
121
+ + constraints = []
122
+ + b_vector = []
123
+ +
124
+ + # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
125
+ for i in range(n):
126
+ - x, y = centers[i]
127
+ - # Distance to borders
128
+ - radii[i] = min(x, y, 1 - x, 1 - y)
129
+ + # r_i <= x_i
130
+ + row = np.zeros(n)
131
+ + row[i] = 1
132
+ + constraints.append(row)
133
+ + b_vector.append(centers[i, 0])
134
+
135
+ - # Then, limit by distance to other circles
136
+ - # Each pair of circles with centers at distance d can have
137
+ - # sum of radii at most d to avoid overlap
138
+ + # r_i <= 1 - x_i
139
+ + row = np.zeros(n)
140
+ + row[i] = 1
141
+ + constraints.append(row)
142
+ + b_vector.append(1 - centers[i, 0])
143
+ +
144
+ + # r_i <= y_i
145
+ + row = np.zeros(n)
146
+ + row[i] = 1
147
+ + constraints.append(row)
148
+ + b_vector.append(centers[i, 1])
149
+ +
150
+ + # r_i <= 1 - y_i
151
+ + row = np.zeros(n)
152
+ + row[i] = 1
153
+ + constraints.append(row)
154
+ + b_vector.append(1 - centers[i, 1])
155
+ +
156
+ + # Pair constraints: r_i + r_j <= d_ij
157
+ for i in range(n):
158
+ for j in range(i + 1, n):
159
+ - dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
160
+ + dist = np.linalg.norm(centers[i] - centers[j])
161
+ + row = np.zeros(n)
162
+ + row[i] = 1
163
+ + row[j] = 1
164
+ + constraints.append(row)
165
+ + b_vector.append(dist)
166
+
167
+ - # If current radii would cause overlap
168
+ - if radii[i] + radii[j] > dist:
169
+ - # Scale both radii proportionally
170
+ - scale = dist / (radii[i] + radii[j])
171
+ - radii[i] *= scale
172
+ - radii[j] *= scale
173
+ + A_ub = np.array(constraints)
174
+ + b_ub = np.array(b_vector)
175
+
176
+ - return radii
177
+ + # All radii must be non-negative.
178
+ + bounds = [(0, None) for _ in range(n)]
179
+ +
180
+ + # Solve the linear program. 'highs' is a fast and reliable solver.
181
+ + res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
182
+ +
183
+ + if res.success:
184
+ + # Return the optimal radii
185
+ + return res.x
186
+ + else:
187
+ + # Fallback in case of solver failure
188
+ + # An empty array will result in a sum of 0, signaling a problem.
189
+ + return np.zeros(n)
190
+
191
+
192
+ # EVOLVE-BLOCK-END
193
+
194
+
195
+ # This part remains fixed (not evolved)
196
+ def run_packing():
197
+ """Run the circle packing constructor for n=26"""
198
+ centers, radii = construct_packing()
199
+ # Calculate the sum of radii
200
+ sum_radii = np.sum(radii)
201
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_1/main.py ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
4
+ and linear programming for optimal radii.
5
+ """
6
+
7
+ import numpy as np
8
+ from scipy.optimize import linprog
9
+
10
+
11
+ def construct_packing():
12
+ """
13
+ Constructs a packing of 26 circles based on a hierarchical grid structure.
14
+ The radii are then optimized using a linear programming solver to maximize their sum.
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
+ # Parameters for the hierarchical grid. These are chosen to create a
25
+ # balanced initial configuration. R is the conceptual radius for the main
26
+ # grid, which determines the overall scale. d splits the central gap.
27
+ R = 0.121 # Base radius for the 4x4 grid
28
+ d = 0.055 # Displacement for the two central circles
29
+
30
+ # Center the entire structure within the unit square
31
+ margin = (1.0 - 8 * R) / 2.0
32
+
33
+ k = 0
34
+
35
+ # 1. Place 16 primary circles in a 4x4 grid
36
+ for i in range(4):
37
+ for j in range(4):
38
+ x = margin + (2 * i + 1) * R
39
+ y = margin + (2 * j + 1) * R
40
+ centers[k] = [x, y]
41
+ k += 1
42
+
43
+ # 2. Place 8 secondary circles in the 3x3 interstitial gaps, skipping the center
44
+ for i in range(3):
45
+ for j in range(3):
46
+ if i == 1 and j == 1:
47
+ continue
48
+ x = margin + (2 * (i + 1)) * R
49
+ y = margin + (2 * (j + 1)) * R
50
+ centers[k] = [x, y]
51
+ k += 1
52
+
53
+ # 3. Place 2 tertiary circles in the central gap, split by 'd'
54
+ center_point = margin + 4 * R
55
+ centers[k] = [center_point, center_point - d]
56
+ k += 1
57
+ centers[k] = [center_point, center_point + d]
58
+ k += 1
59
+
60
+ # For the given centers, compute the radii that maximize the sum.
61
+ radii = compute_max_radii(centers)
62
+
63
+ return centers, radii
64
+
65
+
66
+ def compute_max_radii(centers):
67
+ """
68
+ Computes the maximum possible radii for a given set of circle centers
69
+ by solving a linear programming problem. This maximizes the sum of radii
70
+ subject to non-overlapping and boundary constraints.
71
+
72
+ Args:
73
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
74
+
75
+ Returns:
76
+ np.array of shape (n) with the optimal radius for each circle.
77
+ """
78
+ n = centers.shape[0]
79
+
80
+ # The objective is to maximize sum(radii), which is equivalent to
81
+ # minimizing sum(-radii).
82
+ c = -np.ones(n)
83
+
84
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
85
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
86
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
87
+ constraints = []
88
+ b_vector = []
89
+
90
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
91
+ for i in range(n):
92
+ # r_i <= x_i
93
+ row = np.zeros(n)
94
+ row[i] = 1
95
+ constraints.append(row)
96
+ b_vector.append(centers[i, 0])
97
+
98
+ # r_i <= 1 - x_i
99
+ row = np.zeros(n)
100
+ row[i] = 1
101
+ constraints.append(row)
102
+ b_vector.append(1 - centers[i, 0])
103
+
104
+ # r_i <= y_i
105
+ row = np.zeros(n)
106
+ row[i] = 1
107
+ constraints.append(row)
108
+ b_vector.append(centers[i, 1])
109
+
110
+ # r_i <= 1 - y_i
111
+ row = np.zeros(n)
112
+ row[i] = 1
113
+ constraints.append(row)
114
+ b_vector.append(1 - centers[i, 1])
115
+
116
+ # Pair constraints: r_i + r_j <= d_ij
117
+ for i in range(n):
118
+ for j in range(i + 1, n):
119
+ dist = np.linalg.norm(centers[i] - centers[j])
120
+ row = np.zeros(n)
121
+ row[i] = 1
122
+ row[j] = 1
123
+ constraints.append(row)
124
+ b_vector.append(dist)
125
+
126
+ A_ub = np.array(constraints)
127
+ b_ub = np.array(b_vector)
128
+
129
+ # All radii must be non-negative.
130
+ bounds = [(0, None) for _ in range(n)]
131
+
132
+ # Solve the linear program. 'highs' is a fast and reliable solver.
133
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
134
+
135
+ if res.success:
136
+ # Return the optimal radii
137
+ return res.x
138
+ else:
139
+ # Fallback in case of solver failure
140
+ # An empty array will result in a sum of 0, signaling a problem.
141
+ return np.zeros(n)
142
+
143
+
144
+ # EVOLVE-BLOCK-END
145
+
146
+
147
+ # This part remains fixed (not evolved)
148
+ def run_packing():
149
+ """Run the circle packing constructor for n=26"""
150
+ centers, radii = construct_packing()
151
+ # Calculate the sum of radii
152
+ sum_radii = np.sum(radii)
153
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/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/results_full_gen200_period20_20260207_182944/gen_1/rewrite.txt ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
4
+ and linear programming for optimal radii.
5
+ """
6
+
7
+ import numpy as np
8
+ from scipy.optimize import linprog
9
+
10
+
11
+ def construct_packing():
12
+ """
13
+ Constructs a packing of 26 circles based on a hierarchical grid structure.
14
+ The radii are then optimized using a linear programming solver to maximize their sum.
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
+ # Parameters for the hierarchical grid. These are chosen to create a
25
+ # balanced initial configuration. R is the conceptual radius for the main
26
+ # grid, which determines the overall scale. d splits the central gap.
27
+ R = 0.121 # Base radius for the 4x4 grid
28
+ d = 0.055 # Displacement for the two central circles
29
+
30
+ # Center the entire structure within the unit square
31
+ margin = (1.0 - 8 * R) / 2.0
32
+
33
+ k = 0
34
+
35
+ # 1. Place 16 primary circles in a 4x4 grid
36
+ for i in range(4):
37
+ for j in range(4):
38
+ x = margin + (2 * i + 1) * R
39
+ y = margin + (2 * j + 1) * R
40
+ centers[k] = [x, y]
41
+ k += 1
42
+
43
+ # 2. Place 8 secondary circles in the 3x3 interstitial gaps, skipping the center
44
+ for i in range(3):
45
+ for j in range(3):
46
+ if i == 1 and j == 1:
47
+ continue
48
+ x = margin + (2 * (i + 1)) * R
49
+ y = margin + (2 * (j + 1)) * R
50
+ centers[k] = [x, y]
51
+ k += 1
52
+
53
+ # 3. Place 2 tertiary circles in the central gap, split by 'd'
54
+ center_point = margin + 4 * R
55
+ centers[k] = [center_point, center_point - d]
56
+ k += 1
57
+ centers[k] = [center_point, center_point + d]
58
+ k += 1
59
+
60
+ # For the given centers, compute the radii that maximize the sum.
61
+ radii = compute_max_radii(centers)
62
+
63
+ return centers, radii
64
+
65
+
66
+ def compute_max_radii(centers):
67
+ """
68
+ Computes the maximum possible radii for a given set of circle centers
69
+ by solving a linear programming problem. This maximizes the sum of radii
70
+ subject to non-overlapping and boundary constraints.
71
+
72
+ Args:
73
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
74
+
75
+ Returns:
76
+ np.array of shape (n) with the optimal radius for each circle.
77
+ """
78
+ n = centers.shape[0]
79
+
80
+ # The objective is to maximize sum(radii), which is equivalent to
81
+ # minimizing sum(-radii).
82
+ c = -np.ones(n)
83
+
84
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
85
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
86
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
87
+ constraints = []
88
+ b_vector = []
89
+
90
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
91
+ for i in range(n):
92
+ # r_i <= x_i
93
+ row = np.zeros(n)
94
+ row[i] = 1
95
+ constraints.append(row)
96
+ b_vector.append(centers[i, 0])
97
+
98
+ # r_i <= 1 - x_i
99
+ row = np.zeros(n)
100
+ row[i] = 1
101
+ constraints.append(row)
102
+ b_vector.append(1 - centers[i, 0])
103
+
104
+ # r_i <= y_i
105
+ row = np.zeros(n)
106
+ row[i] = 1
107
+ constraints.append(row)
108
+ b_vector.append(centers[i, 1])
109
+
110
+ # r_i <= 1 - y_i
111
+ row = np.zeros(n)
112
+ row[i] = 1
113
+ constraints.append(row)
114
+ b_vector.append(1 - centers[i, 1])
115
+
116
+ # Pair constraints: r_i + r_j <= d_ij
117
+ for i in range(n):
118
+ for j in range(i + 1, n):
119
+ dist = np.linalg.norm(centers[i] - centers[j])
120
+ row = np.zeros(n)
121
+ row[i] = 1
122
+ row[j] = 1
123
+ constraints.append(row)
124
+ b_vector.append(dist)
125
+
126
+ A_ub = np.array(constraints)
127
+ b_ub = np.array(b_vector)
128
+
129
+ # All radii must be non-negative.
130
+ bounds = [(0, None) for _ in range(n)]
131
+
132
+ # Solve the linear program. 'highs' is a fast and reliable solver.
133
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
134
+
135
+ if res.success:
136
+ # Return the optimal radii
137
+ return res.x
138
+ else:
139
+ # Fallback in case of solver failure
140
+ # An empty array will result in a sum of 0, signaling a problem.
141
+ return np.zeros(n)
142
+
143
+
144
+ # EVOLVE-BLOCK-END
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/__pycache__/main.cpython-313.pyc ADDED
Binary file (5.32 kB). View file
 
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/edit.diff ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,174 +1,176 @@
4
+ # EVOLVE-BLOCK-START
5
+ """
6
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
7
+ and linear programming for optimal radii. This version incorporates refined
8
+ parameters for the hierarchical grid to maximize the sum of radii.
9
+ """
10
+
11
+ import numpy as np
12
+ from scipy.optimize import linprog
13
+
14
+
15
+ def construct_packing():
16
+ """
17
+ Constructs a packing of 26 circles based on a hierarchical grid structure.
18
+ The radii are then optimized using a linear programming solver to maximize their sum.
19
+
20
+ This implementation performs a 'crossover' by taking the highly effective
21
+ hierarchical grid placement strategy from one program and refining its
22
+ parameters based on analysis of the best performing solution, while retaining
23
+ the robust linear programming approach for radii calculation from all high-performing
24
+ programs.
25
+
26
+ Returns:
27
+ Tuple of (centers, radii)
28
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
29
+ radii: np.array of shape (26) with radius of each circle.
30
+ """
31
+ n = 26
32
+ centers = np.zeros((n, 2))
33
+
34
+ - # Refined parameters for the hierarchical grid. These values are a perturbation
35
+ - # of the previously best-performing parameters (R=0.121, d=0.055), aiming
36
+ - # to better utilize space near the boundaries and optimize central circle separation.
37
+ - R = 0.1225 # Increased slightly to push primary grid circles closer to boundaries
38
+ - d = 0.056 # Adjusted central displacement for potential radius gain
39
+ + # This configuration sets the grid to perfectly fit the unit square, which is
40
+ + # a critical point in the parameter space. R=0.125 means the conceptual
41
+ + # 8x8 grid of centers has a side length of 1.0 (8 * 0.125 = 1.0).
42
+ + R = 0.125
43
+ + # The central displacement `d` is set to 1/16, a value that balances the
44
+ + # key spatial constraints on the two central circles.
45
+ + d = 0.0625
46
+
47
+ # Center the entire structure within the unit square
48
+ # margin ensures the overall grid is centered.
49
+ margin = (1.0 - 8 * R) / 2.0
50
+
51
+ k = 0
52
+
53
+ # 1. Place 16 primary circles in a 4x4 grid
54
+ # These circles are positioned at odd multiples of R from the margin.
55
+ for i in range(4):
56
+ for j in range(4):
57
+ x = margin + (2 * i + 1) * R
58
+ y = margin + (2 * j + 1) * R
59
+ centers[k] = [x, y]
60
+ k += 1
61
+
62
+ # 2. Place 8 secondary circles in the 3x3 interstitial gaps, skipping the center.
63
+ # These are positioned at even multiples of R from the margin.
64
+ for i in range(3):
65
+ for j in range(3):
66
+ # Skip the very central interstitial spot as two other circles will occupy it.
67
+ if i == 1 and j == 1:
68
+ continue
69
+ x = margin + (2 * (i + 1)) * R
70
+ y = margin + (2 * (j + 1)) * R
71
+ centers[k] = [x, y]
72
+ k += 1
73
+
74
+ # 3. Place 2 tertiary circles in the central gap, split by 'd'.
75
+ # These circles break the symmetry at the square's center.
76
+ center_point = margin + 4 * R # This calculates the exact center of the grid structure
77
+ centers[k] = [center_point, center_point - d]
78
+ k += 1
79
+ centers[k] = [center_point, center_point + d]
80
+ k += 1
81
+
82
+ # Ensure centers are strictly within (0,1) to avoid numerical issues
83
+ # at boundaries when calculating radii. This also borrows from concepts
84
+ # in the class-based placement strategy for robustness.
85
+ centers = np.clip(centers, 1e-6, 1 - 1e-6)
86
+
87
+ # For the given centers, compute the radii that maximize the sum using LP.
88
+ radii = compute_max_radii(centers)
89
+
90
+ return centers, radii
91
+
92
+
93
+ def compute_max_radii(centers):
94
+ """
95
+ Computes the maximum possible radii for a given set of circle centers
96
+ by solving a linear programming problem. This maximizes the sum of radii
97
+ subject to non-overlapping and boundary constraints.
98
+ This function is retained from the best performing solution due to its
99
+ mathematical optimality for fixed centers.
100
+
101
+ Args:
102
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
103
+
104
+ Returns:
105
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
106
+ """
107
+ n = centers.shape[0]
108
+
109
+ # The objective is to maximize sum(radii), which is equivalent to
110
+ # minimizing sum(-radii).
111
+ c = -np.ones(n)
112
+
113
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
114
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
115
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
116
+ constraints = []
117
+ b_vector = []
118
+
119
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
120
+ # These ensure circles stay within the unit square.
121
+ for i in range(n):
122
+ # r_i <= x_i
123
+ row = np.zeros(n)
124
+ row[i] = 1
125
+ constraints.append(row)
126
+ b_vector.append(centers[i, 0])
127
+
128
+ # r_i <= 1 - x_i
129
+ row = np.zeros(n)
130
+ row[i] = 1
131
+ constraints.append(row)
132
+ b_vector.append(1 - centers[i, 0])
133
+
134
+ # r_i <= y_i
135
+ row = np.zeros(n)
136
+ row[i] = 1
137
+ constraints.append(row)
138
+ b_vector.append(centers[i, 1])
139
+
140
+ # r_i <= 1 - y_i
141
+ row = np.zeros(n)
142
+ row[i] = 1
143
+ constraints.append(row)
144
+ b_vector.append(1 - centers[i, 1])
145
+
146
+ # Pair constraints: r_i + r_j <= d_ij
147
+ # These prevent circles from overlapping.
148
+ for i in range(n):
149
+ for j in range(i + 1, n):
150
+ dist = np.linalg.norm(centers[i] - centers[j])
151
+ row = np.zeros(n)
152
+ row[i] = 1
153
+ row[j] = 1
154
+ constraints.append(row)
155
+ b_vector.append(dist)
156
+
157
+ A_ub = np.array(constraints)
158
+ b_ub = np.array(b_vector)
159
+
160
+ # All radii must be non-negative.
161
+ # The (0, None) bounds indicate r_i >= 0.
162
+ bounds = [(0, None) for _ in range(n)]
163
+
164
+ # Solve the linear program using the 'highs' solver for performance.
165
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
166
+
167
+ if res.success:
168
+ # Return the optimal radii found by the solver.
169
+ return res.x
170
+ else:
171
+ # Fallback in case of solver failure.
172
+ # Returning zeros implies no valid radii could be determined.
173
+ print(f"LP solver failed: {res.message}")
174
+ return np.zeros(n)
175
+ # EVOLVE-BLOCK-END
176
+
177
+
178
+ # This part remains fixed (not evolved)
179
+ def run_packing():
180
+ """Run the circle packing constructor for n=26"""
181
+ centers, radii = construct_packing()
182
+ # Calculate the sum of radii
183
+ sum_radii = np.sum(radii)
184
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/main.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
4
+ and linear programming for optimal radii. This version incorporates refined
5
+ parameters for the hierarchical grid to maximize the sum of radii.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+
11
+
12
+ def construct_packing():
13
+ """
14
+ Constructs a packing of 26 circles based on a hierarchical grid structure.
15
+ The radii are then optimized using a linear programming solver to maximize their sum.
16
+
17
+ This implementation performs a 'crossover' by taking the highly effective
18
+ hierarchical grid placement strategy from one program and refining its
19
+ parameters based on analysis of the best performing solution, while retaining
20
+ the robust linear programming approach for radii calculation from all high-performing
21
+ programs.
22
+
23
+ Returns:
24
+ Tuple of (centers, radii)
25
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
26
+ radii: np.array of shape (26) with radius of each circle.
27
+ """
28
+ n = 26
29
+ centers = np.zeros((n, 2))
30
+
31
+ # This configuration sets the grid to perfectly fit the unit square, which is
32
+ # a critical point in the parameter space. R=0.125 means the conceptual
33
+ # 8x8 grid of centers has a side length of 1.0 (8 * 0.125 = 1.0).
34
+ R = 0.125
35
+ # The central displacement `d` is set to 1/16, a value that balances the
36
+ # key spatial constraints on the two central circles.
37
+ d = 0.0625
38
+
39
+ # Center the entire structure within the unit square
40
+ # margin ensures the overall grid is centered.
41
+ margin = (1.0 - 8 * R) / 2.0
42
+
43
+ k = 0
44
+
45
+ # 1. Place 16 primary circles in a 4x4 grid
46
+ # These circles are positioned at odd multiples of R from the margin.
47
+ for i in range(4):
48
+ for j in range(4):
49
+ x = margin + (2 * i + 1) * R
50
+ y = margin + (2 * j + 1) * R
51
+ centers[k] = [x, y]
52
+ k += 1
53
+
54
+ # 2. Place 8 secondary circles in the 3x3 interstitial gaps, skipping the center.
55
+ # These are positioned at even multiples of R from the margin.
56
+ for i in range(3):
57
+ for j in range(3):
58
+ # Skip the very central interstitial spot as two other circles will occupy it.
59
+ if i == 1 and j == 1:
60
+ continue
61
+ x = margin + (2 * (i + 1)) * R
62
+ y = margin + (2 * (j + 1)) * R
63
+ centers[k] = [x, y]
64
+ k += 1
65
+
66
+ # 3. Place 2 tertiary circles in the central gap, split by 'd'.
67
+ # These circles break the symmetry at the square's center.
68
+ center_point = margin + 4 * R # This calculates the exact center of the grid structure
69
+ centers[k] = [center_point, center_point - d]
70
+ k += 1
71
+ centers[k] = [center_point, center_point + d]
72
+ k += 1
73
+
74
+ # Ensure centers are strictly within (0,1) to avoid numerical issues
75
+ # at boundaries when calculating radii. This also borrows from concepts
76
+ # in the class-based placement strategy for robustness.
77
+ centers = np.clip(centers, 1e-6, 1 - 1e-6)
78
+
79
+ # For the given centers, compute the radii that maximize the sum using LP.
80
+ radii = compute_max_radii(centers)
81
+
82
+ return centers, radii
83
+
84
+
85
+ def compute_max_radii(centers):
86
+ """
87
+ Computes the maximum possible radii for a given set of circle centers
88
+ by solving a linear programming problem. This maximizes the sum of radii
89
+ subject to non-overlapping and boundary constraints.
90
+ This function is retained from the best performing solution due to its
91
+ mathematical optimality for fixed centers.
92
+
93
+ Args:
94
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
95
+
96
+ Returns:
97
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
98
+ """
99
+ n = centers.shape[0]
100
+
101
+ # The objective is to maximize sum(radii), which is equivalent to
102
+ # minimizing sum(-radii).
103
+ c = -np.ones(n)
104
+
105
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
106
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
107
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
108
+ constraints = []
109
+ b_vector = []
110
+
111
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
112
+ # These ensure circles stay within the unit square.
113
+ for i in range(n):
114
+ # r_i <= x_i
115
+ row = np.zeros(n)
116
+ row[i] = 1
117
+ constraints.append(row)
118
+ b_vector.append(centers[i, 0])
119
+
120
+ # r_i <= 1 - x_i
121
+ row = np.zeros(n)
122
+ row[i] = 1
123
+ constraints.append(row)
124
+ b_vector.append(1 - centers[i, 0])
125
+
126
+ # r_i <= y_i
127
+ row = np.zeros(n)
128
+ row[i] = 1
129
+ constraints.append(row)
130
+ b_vector.append(centers[i, 1])
131
+
132
+ # r_i <= 1 - y_i
133
+ row = np.zeros(n)
134
+ row[i] = 1
135
+ constraints.append(row)
136
+ b_vector.append(1 - centers[i, 1])
137
+
138
+ # Pair constraints: r_i + r_j <= d_ij
139
+ # These prevent circles from overlapping.
140
+ for i in range(n):
141
+ for j in range(i + 1, n):
142
+ dist = np.linalg.norm(centers[i] - centers[j])
143
+ row = np.zeros(n)
144
+ row[i] = 1
145
+ row[j] = 1
146
+ constraints.append(row)
147
+ b_vector.append(dist)
148
+
149
+ A_ub = np.array(constraints)
150
+ b_ub = np.array(b_vector)
151
+
152
+ # All radii must be non-negative.
153
+ # The (0, None) bounds indicate r_i >= 0.
154
+ bounds = [(0, None) for _ in range(n)]
155
+
156
+ # Solve the linear program using the 'highs' solver for performance.
157
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
158
+
159
+ if res.success:
160
+ # Return the optimal radii found by the solver.
161
+ return res.x
162
+ else:
163
+ # Fallback in case of solver failure.
164
+ # Returning zeros implies no valid radii could be determined.
165
+ print(f"LP solver failed: {res.message}")
166
+ return np.zeros(n)
167
+ # EVOLVE-BLOCK-END
168
+
169
+
170
+ # This part remains fixed (not evolved)
171
+ def run_packing():
172
+ """Run the circle packing constructor for n=26"""
173
+ centers, radii = construct_packing()
174
+ # Calculate the sum of radii
175
+ sum_radii = np.sum(radii)
176
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/original.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
4
+ and linear programming for optimal radii. This version incorporates refined
5
+ parameters for the hierarchical grid to maximize the sum of radii.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+
11
+
12
+ def construct_packing():
13
+ """
14
+ Constructs a packing of 26 circles based on a hierarchical grid structure.
15
+ The radii are then optimized using a linear programming solver to maximize their sum.
16
+
17
+ This implementation performs a 'crossover' by taking the highly effective
18
+ hierarchical grid placement strategy from one program and refining its
19
+ parameters based on analysis of the best performing solution, while retaining
20
+ the robust linear programming approach for radii calculation from all high-performing
21
+ programs.
22
+
23
+ Returns:
24
+ Tuple of (centers, radii)
25
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
26
+ radii: np.array of shape (26) with radius of each circle.
27
+ """
28
+ n = 26
29
+ centers = np.zeros((n, 2))
30
+
31
+ # Refined parameters for the hierarchical grid. These values are a perturbation
32
+ # of the previously best-performing parameters (R=0.121, d=0.055), aiming
33
+ # to better utilize space near the boundaries and optimize central circle separation.
34
+ R = 0.1225 # Increased slightly to push primary grid circles closer to boundaries
35
+ d = 0.056 # Adjusted central displacement for potential radius gain
36
+
37
+ # Center the entire structure within the unit square
38
+ # margin ensures the overall grid is centered.
39
+ margin = (1.0 - 8 * R) / 2.0
40
+
41
+ k = 0
42
+
43
+ # 1. Place 16 primary circles in a 4x4 grid
44
+ # These circles are positioned at odd multiples of R from the margin.
45
+ for i in range(4):
46
+ for j in range(4):
47
+ x = margin + (2 * i + 1) * R
48
+ y = margin + (2 * j + 1) * R
49
+ centers[k] = [x, y]
50
+ k += 1
51
+
52
+ # 2. Place 8 secondary circles in the 3x3 interstitial gaps, skipping the center.
53
+ # These are positioned at even multiples of R from the margin.
54
+ for i in range(3):
55
+ for j in range(3):
56
+ # Skip the very central interstitial spot as two other circles will occupy it.
57
+ if i == 1 and j == 1:
58
+ continue
59
+ x = margin + (2 * (i + 1)) * R
60
+ y = margin + (2 * (j + 1)) * R
61
+ centers[k] = [x, y]
62
+ k += 1
63
+
64
+ # 3. Place 2 tertiary circles in the central gap, split by 'd'.
65
+ # These circles break the symmetry at the square's center.
66
+ center_point = margin + 4 * R # This calculates the exact center of the grid structure
67
+ centers[k] = [center_point, center_point - d]
68
+ k += 1
69
+ centers[k] = [center_point, center_point + d]
70
+ k += 1
71
+
72
+ # Ensure centers are strictly within (0,1) to avoid numerical issues
73
+ # at boundaries when calculating radii. This also borrows from concepts
74
+ # in the class-based placement strategy for robustness.
75
+ centers = np.clip(centers, 1e-6, 1 - 1e-6)
76
+
77
+ # For the given centers, compute the radii that maximize the sum using LP.
78
+ radii = compute_max_radii(centers)
79
+
80
+ return centers, radii
81
+
82
+
83
+ def compute_max_radii(centers):
84
+ """
85
+ Computes the maximum possible radii for a given set of circle centers
86
+ by solving a linear programming problem. This maximizes the sum of radii
87
+ subject to non-overlapping and boundary constraints.
88
+ This function is retained from the best performing solution due to its
89
+ mathematical optimality for fixed centers.
90
+
91
+ Args:
92
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
93
+
94
+ Returns:
95
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
96
+ """
97
+ n = centers.shape[0]
98
+
99
+ # The objective is to maximize sum(radii), which is equivalent to
100
+ # minimizing sum(-radii).
101
+ c = -np.ones(n)
102
+
103
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
104
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
105
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
106
+ constraints = []
107
+ b_vector = []
108
+
109
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
110
+ # These ensure circles stay within the unit square.
111
+ for i in range(n):
112
+ # r_i <= x_i
113
+ row = np.zeros(n)
114
+ row[i] = 1
115
+ constraints.append(row)
116
+ b_vector.append(centers[i, 0])
117
+
118
+ # r_i <= 1 - x_i
119
+ row = np.zeros(n)
120
+ row[i] = 1
121
+ constraints.append(row)
122
+ b_vector.append(1 - centers[i, 0])
123
+
124
+ # r_i <= y_i
125
+ row = np.zeros(n)
126
+ row[i] = 1
127
+ constraints.append(row)
128
+ b_vector.append(centers[i, 1])
129
+
130
+ # r_i <= 1 - y_i
131
+ row = np.zeros(n)
132
+ row[i] = 1
133
+ constraints.append(row)
134
+ b_vector.append(1 - centers[i, 1])
135
+
136
+ # Pair constraints: r_i + r_j <= d_ij
137
+ # These prevent circles from overlapping.
138
+ for i in range(n):
139
+ for j in range(i + 1, n):
140
+ dist = np.linalg.norm(centers[i] - centers[j])
141
+ row = np.zeros(n)
142
+ row[i] = 1
143
+ row[j] = 1
144
+ constraints.append(row)
145
+ b_vector.append(dist)
146
+
147
+ A_ub = np.array(constraints)
148
+ b_ub = np.array(b_vector)
149
+
150
+ # All radii must be non-negative.
151
+ # The (0, None) bounds indicate r_i >= 0.
152
+ bounds = [(0, None) for _ in range(n)]
153
+
154
+ # Solve the linear program using the 'highs' solver for performance.
155
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
156
+
157
+ if res.success:
158
+ # Return the optimal radii found by the solver.
159
+ return res.x
160
+ else:
161
+ # Fallback in case of solver failure.
162
+ # Returning zeros implies no valid radii could be determined.
163
+ print(f"LP solver failed: {res.message}")
164
+ return np.zeros(n)
165
+ # EVOLVE-BLOCK-END
166
+
167
+
168
+ # This part remains fixed (not evolved)
169
+ def run_packing():
170
+ """Run the circle packing constructor for n=26"""
171
+ centers, radii = construct_packing()
172
+ # Calculate the sum of radii
173
+ sum_radii = np.sum(radii)
174
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/results/correct.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "correct": true,
3
+ "error": null
4
+ }
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/results/metrics.json ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "combined_score": 2.443722059560568,
3
+ "correct": true,
4
+ "primary": {
5
+ "combined_score": 2.443722059560568,
6
+ "public": {
7
+ "centers_str": " centers[0] = (0.1250, 0.1250)\n centers[1] = (0.1250, 0.3750)\n centers[2] = (0.1250, 0.6250)\n centers[3] = (0.1250, 0.8750)\n centers[4] = (0.3750, 0.1250)\n centers[5] = (0.3750, 0.3750)\n centers[6] = (0.3750, 0.6250)\n centers[7] = (0.3750, 0.8750)\n centers[8] = (0.6250, 0.1250)\n centers[9] = (0.6250, 0.3750)\n centers[10] = (0.6250, 0.6250)\n centers[11] = (0.6250, 0.8750)\n centers[12] = (0.8750, 0.1250)\n centers[13] = (0.8750, 0.3750)\n centers[14] = (0.8750, 0.6250)\n centers[15] = (0.8750, 0.8750)\n centers[16] = (0.2500, 0.2500)\n centers[17] = (0.2500, 0.5000)\n centers[18] = (0.2500, 0.7500)\n centers[19] = (0.5000, 0.2500)\n centers[20] = (0.5000, 0.7500)\n centers[21] = (0.7500, 0.2500)\n centers[22] = (0.7500, 0.5000)\n centers[23] = (0.7500, 0.7500)\n centers[24] = (0.5000, 0.4375)\n centers[25] = (0.5000, 0.5625)",
8
+ "num_circles": 26
9
+ },
10
+ "private": {
11
+ "reported_sum_of_radii": 2.443722059560568
12
+ },
13
+ "execution_time_mean": 0.04552629217505455,
14
+ "execution_time_std": 0.0,
15
+ "num_valid_runs": 1,
16
+ "num_invalid_runs": 0,
17
+ "all_validation_errors": [],
18
+ "correct": true,
19
+ "validation_error": null
20
+ },
21
+ "auxiliary": {},
22
+ "auxiliary_descriptions": {},
23
+ "timestamp": 1770490043.661491,
24
+ "generation": 10
25
+ }
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_10/search_replace.txt ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <NAME>
2
+ grid_perfect_fit
3
+ </NAME>
4
+ <DESCRIPTION>
5
+ The current best strategy uses a hierarchical grid with parameters `R` and `d`. The performance has been improving as `R` increases, pushing the grid outwards. This edit takes that trend to its logical conclusion by setting `R=0.125`. This specific value makes the conceptual 8x8 grid of circle centers (from the 4x4 primary grid) have a total width of `8 * R = 1.0`, meaning the grid structure perfectly fits the unit square with zero margin. This is a critical configuration that forces the outer circles to be tangent to the boundaries, maximizing their potential size.
6
+
7
+ With this change in `R`, the parameter `d` for the two central circles is also re-evaluated. The central circles are constrained by each other (distance `2d`) and by the surrounding interstitial circles. A new value of `d=0.0625` (which is 1/16) is chosen. This value is derived by balancing the main constraints on the central circles' radii: the constraint from their twin (`r_central <= d`) and the constraint from the next interstitial circle along the y-axis (`r_central` is also limited by the space `0.25 - d`). Setting the bounds `d` and `0.125-d` to be equal gives `d=0.0625`, a choice that heuristically equalizes the spatial pressure on these central circles.
8
+ </DESCRIPTION>
9
+ <DIFF>
10
+ <<<<<<< SEARCH
11
+ # Refined parameters for the hierarchical grid. These values are a perturbation
12
+ # of the previously best-performing parameters (R=0.121, d=0.055), aiming
13
+ # to better utilize space near the boundaries and optimize central circle separation.
14
+ R = 0.1225 # Increased slightly to push primary grid circles closer to boundaries
15
+ d = 0.056 # Adjusted central displacement for potential radius gain
16
+ =======
17
+ # This configuration sets the grid to perfectly fit the unit square, which is
18
+ # a critical point in the parameter space. R=0.125 means the conceptual
19
+ # 8x8 grid of centers has a side length of 1.0 (8 * 0.125 = 1.0).
20
+ R = 0.125
21
+ # The central displacement `d` is set to 1/16, a value that balances the
22
+ # key spatial constraints on the two central circles.
23
+ d = 0.0625
24
+ >>>>>>> REPLACE
25
+ </DIFF>
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_100/edit.diff ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,170 +1,174 @@
4
+ # EVOLVE-BLOCK-START
5
+ """
6
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
7
+ and linear programming for optimal radii. This version incorporates refined
8
+ parameters for the hierarchical grid to maximize the sum of radii.
9
+ """
10
+
11
+ import numpy as np
12
+ from scipy.optimize import linprog
13
+ import math # Imported for trigonometric functions in central placement
14
+
15
+
16
+ def construct_packing():
17
+ """
18
+ Constructs a packing of 26 circles by performing a crossover between two
19
+ successful strategies. It uses a 5x5 grid base and incorporates a superior
20
+ central placement logic.
21
+
22
+ The base structure is a 5x5 grid with the center removed, from the "Current Program".
23
+ This provides a robust scaffold for 24 circles.
24
+
25
+ The crossover improvement comes from replacing the simple vertical split for the
26
+ two central circles with the parameterized asymmetric diagonal placement from the
27
+ "Inspiration Program", which is proven to yield a higher score.
28
+
29
+ Returns:
30
+ Tuple of (centers, radii)
31
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
32
+ radii: np.array of shape (26) with radius of each circle.
33
+ """
34
+ n = 26
35
+ centers = np.zeros((n, 2))
36
+ k = 0
37
+
38
+ - # 1. Place 24 circles in a 5x5 grid, skipping the central point.
39
+ - # This structure is inherited from the effective "Current Program".
40
+ + # 1. Place 24 circles in a non-uniform 5x5 grid, skipping the central point.
41
+ + # The grid is expanded in the center to create more space for the central
42
+ + # circles and the circles on the medial axes. This is a trade-off, as it
43
+ + # reduces space for circles in the outer grid regions.
44
+ num_grid_divs = 5
45
+ - coords = np.linspace(0.1, 0.9, num_grid_divs)
46
+ + # Original coords: np.linspace(0.1, 0.9, 5) -> [0.1, 0.3, 0.5, 0.7, 0.9]
47
+ + # We move the inner grid lines (0.3, 0.7) outwards to (0.28, 0.72).
48
+ + coords = np.array([0.1, 0.28, 0.5, 0.72, 0.9])
49
+
50
+ for i in range(num_grid_divs):
51
+ for j in range(num_grid_divs):
52
+ # Skip the center of the 5x5 grid, which is at index (2, 2)
53
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
54
+ continue
55
+ centers[k] = [coords[i], coords[j]]
56
+ k += 1
57
+
58
+ - # 2. Crossover: Place 2 circles in the central gap using the superior logic
59
+ - # from the "Inspiration Program".
60
+ - # Parameters are taken from the best-performing variant (score 2.51).
61
+ - central_separation_distance = 0.107
62
+ + # 2. Crossover: Place 2 circles in the now larger central gap.
63
+ + # The separation distance is increased to utilize the extra space created by
64
+ + # the non-uniform grid. The asymmetry angle is kept.
65
+ + central_separation_distance = 0.120
66
+ central_asymmetry_angle_deg = 44.5
67
+
68
+ # R_prime is the distance from the square's center (0.5, 0.5) to each central circle's center.
69
+ R_prime = central_separation_distance / 2.0
70
+ angle_rad = math.radians(central_asymmetry_angle_deg)
71
+
72
+ # Calculate x and y offsets for the asymmetric diagonal placement.
73
+ dx = R_prime * math.cos(angle_rad)
74
+ dy = R_prime * math.sin(angle_rad)
75
+
76
+ center_point = 0.5
77
+ centers[k] = [center_point - dx, center_point - dy]
78
+ k += 1
79
+ centers[k] = [center_point + dx, center_point + dy]
80
+ k += 1
81
+
82
+
83
+ # Ensure centers are strictly within (0,1) to avoid numerical issues
84
+ # at boundaries when calculating radii.
85
+ centers = np.clip(centers, 1e-6, 1 - 1e-6)
86
+
87
+ # For the given centers, compute the radii that maximize the sum using LP.
88
+ radii = compute_max_radii(centers)
89
+
90
+ return centers, radii
91
+
92
+
93
+ def compute_max_radii(centers):
94
+ """
95
+ Computes the maximum possible radii for a given set of circle centers
96
+ by solving a linear programming problem. This maximizes the sum of radii
97
+ subject to non-overlapping and boundary constraints.
98
+ This function is retained from the best performing solution due to its
99
+ mathematical optimality for fixed centers.
100
+
101
+ Args:
102
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
103
+
104
+ Returns:
105
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
106
+ """
107
+ n = centers.shape[0]
108
+
109
+ # The objective is to maximize sum(radii), which is equivalent to
110
+ # minimizing sum(-radii).
111
+ c = -np.ones(n)
112
+
113
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
114
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
115
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
116
+ constraints = []
117
+ b_vector = []
118
+
119
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
120
+ # These ensure circles stay within the unit square.
121
+ for i in range(n):
122
+ # r_i <= x_i
123
+ row = np.zeros(n)
124
+ row[i] = 1
125
+ constraints.append(row)
126
+ b_vector.append(centers[i, 0])
127
+
128
+ # r_i <= 1 - x_i
129
+ row = np.zeros(n)
130
+ row[i] = 1
131
+ constraints.append(row)
132
+ b_vector.append(1 - centers[i, 0])
133
+
134
+ # r_i <= y_i
135
+ row = np.zeros(n)
136
+ row[i] = 1
137
+ constraints.append(row)
138
+ b_vector.append(centers[i, 1])
139
+
140
+ # r_i <= 1 - y_i
141
+ row = np.zeros(n)
142
+ row[i] = 1
143
+ constraints.append(row)
144
+ b_vector.append(1 - centers[i, 1])
145
+
146
+ # Pair constraints: r_i + r_j <= d_ij
147
+ # These prevent circles from overlapping.
148
+ for i in range(n):
149
+ for j in range(i + 1, n):
150
+ dist = np.linalg.norm(centers[i] - centers[j])
151
+ row = np.zeros(n)
152
+ row[i] = 1
153
+ row[j] = 1
154
+ constraints.append(row)
155
+ b_vector.append(dist)
156
+
157
+ A_ub = np.array(constraints)
158
+ b_ub = np.array(b_vector)
159
+
160
+ # All radii must be non-negative.
161
+ # The (0, None) bounds indicate r_i >= 0.
162
+ bounds = [(0, None) for _ in range(n)]
163
+
164
+ # Solve the linear program using the 'highs' solver for performance.
165
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
166
+
167
+ if res.success:
168
+ # Return the optimal radii found by the solver.
169
+ return res.x
170
+ else:
171
+ # Fallback in case of solver failure.
172
+ # Returning zeros implies no valid radii could be determined.
173
+ print(f"LP solver failed: {res.message}")
174
+ return np.zeros(n)
175
+ # EVOLVE-BLOCK-END
176
+
177
+
178
+ # This part remains fixed (not evolved)
179
+ def run_packing():
180
+ """Run the circle packing constructor for n=26"""
181
+ centers, radii = construct_packing()
182
+ # Calculate the sum of radii
183
+ sum_radii = np.sum(radii)
184
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_100/main.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
4
+ and linear programming for optimal radii. This version incorporates refined
5
+ parameters for the hierarchical grid to maximize the sum of radii.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+ import math # Imported for trigonometric functions in central placement
11
+
12
+
13
+ def construct_packing():
14
+ """
15
+ Constructs a packing of 26 circles by performing a crossover between two
16
+ successful strategies. It uses a 5x5 grid base and incorporates a superior
17
+ central placement logic.
18
+
19
+ The base structure is a 5x5 grid with the center removed, from the "Current Program".
20
+ This provides a robust scaffold for 24 circles.
21
+
22
+ The crossover improvement comes from replacing the simple vertical split for the
23
+ two central circles with the parameterized asymmetric diagonal placement from the
24
+ "Inspiration Program", which is proven to yield a higher score.
25
+
26
+ Returns:
27
+ Tuple of (centers, radii)
28
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
29
+ radii: np.array of shape (26) with radius of each circle.
30
+ """
31
+ n = 26
32
+ centers = np.zeros((n, 2))
33
+ k = 0
34
+
35
+ # 1. Place 24 circles in a non-uniform 5x5 grid, skipping the central point.
36
+ # The grid is expanded in the center to create more space for the central
37
+ # circles and the circles on the medial axes. This is a trade-off, as it
38
+ # reduces space for circles in the outer grid regions.
39
+ num_grid_divs = 5
40
+ # Original coords: np.linspace(0.1, 0.9, 5) -> [0.1, 0.3, 0.5, 0.7, 0.9]
41
+ # We move the inner grid lines (0.3, 0.7) outwards to (0.28, 0.72).
42
+ coords = np.array([0.1, 0.28, 0.5, 0.72, 0.9])
43
+
44
+ for i in range(num_grid_divs):
45
+ for j in range(num_grid_divs):
46
+ # Skip the center of the 5x5 grid, which is at index (2, 2)
47
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
48
+ continue
49
+ centers[k] = [coords[i], coords[j]]
50
+ k += 1
51
+
52
+ # 2. Crossover: Place 2 circles in the now larger central gap.
53
+ # The separation distance is increased to utilize the extra space created by
54
+ # the non-uniform grid. The asymmetry angle is kept.
55
+ central_separation_distance = 0.120
56
+ central_asymmetry_angle_deg = 44.5
57
+
58
+ # R_prime is the distance from the square's center (0.5, 0.5) to each central circle's center.
59
+ R_prime = central_separation_distance / 2.0
60
+ angle_rad = math.radians(central_asymmetry_angle_deg)
61
+
62
+ # Calculate x and y offsets for the asymmetric diagonal placement.
63
+ dx = R_prime * math.cos(angle_rad)
64
+ dy = R_prime * math.sin(angle_rad)
65
+
66
+ center_point = 0.5
67
+ centers[k] = [center_point - dx, center_point - dy]
68
+ k += 1
69
+ centers[k] = [center_point + dx, center_point + dy]
70
+ k += 1
71
+
72
+
73
+ # Ensure centers are strictly within (0,1) to avoid numerical issues
74
+ # at boundaries when calculating radii.
75
+ centers = np.clip(centers, 1e-6, 1 - 1e-6)
76
+
77
+ # For the given centers, compute the radii that maximize the sum using LP.
78
+ radii = compute_max_radii(centers)
79
+
80
+ return centers, radii
81
+
82
+
83
+ def compute_max_radii(centers):
84
+ """
85
+ Computes the maximum possible radii for a given set of circle centers
86
+ by solving a linear programming problem. This maximizes the sum of radii
87
+ subject to non-overlapping and boundary constraints.
88
+ This function is retained from the best performing solution due to its
89
+ mathematical optimality for fixed centers.
90
+
91
+ Args:
92
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
93
+
94
+ Returns:
95
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
96
+ """
97
+ n = centers.shape[0]
98
+
99
+ # The objective is to maximize sum(radii), which is equivalent to
100
+ # minimizing sum(-radii).
101
+ c = -np.ones(n)
102
+
103
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
104
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
105
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
106
+ constraints = []
107
+ b_vector = []
108
+
109
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
110
+ # These ensure circles stay within the unit square.
111
+ for i in range(n):
112
+ # r_i <= x_i
113
+ row = np.zeros(n)
114
+ row[i] = 1
115
+ constraints.append(row)
116
+ b_vector.append(centers[i, 0])
117
+
118
+ # r_i <= 1 - x_i
119
+ row = np.zeros(n)
120
+ row[i] = 1
121
+ constraints.append(row)
122
+ b_vector.append(1 - centers[i, 0])
123
+
124
+ # r_i <= y_i
125
+ row = np.zeros(n)
126
+ row[i] = 1
127
+ constraints.append(row)
128
+ b_vector.append(centers[i, 1])
129
+
130
+ # r_i <= 1 - y_i
131
+ row = np.zeros(n)
132
+ row[i] = 1
133
+ constraints.append(row)
134
+ b_vector.append(1 - centers[i, 1])
135
+
136
+ # Pair constraints: r_i + r_j <= d_ij
137
+ # These prevent circles from overlapping.
138
+ for i in range(n):
139
+ for j in range(i + 1, n):
140
+ dist = np.linalg.norm(centers[i] - centers[j])
141
+ row = np.zeros(n)
142
+ row[i] = 1
143
+ row[j] = 1
144
+ constraints.append(row)
145
+ b_vector.append(dist)
146
+
147
+ A_ub = np.array(constraints)
148
+ b_ub = np.array(b_vector)
149
+
150
+ # All radii must be non-negative.
151
+ # The (0, None) bounds indicate r_i >= 0.
152
+ bounds = [(0, None) for _ in range(n)]
153
+
154
+ # Solve the linear program using the 'highs' solver for performance.
155
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
156
+
157
+ if res.success:
158
+ # Return the optimal radii found by the solver.
159
+ return res.x
160
+ else:
161
+ # Fallback in case of solver failure.
162
+ # Returning zeros implies no valid radii could be determined.
163
+ print(f"LP solver failed: {res.message}")
164
+ return np.zeros(n)
165
+ # EVOLVE-BLOCK-END
166
+
167
+
168
+ # This part remains fixed (not evolved)
169
+ def run_packing():
170
+ """Run the circle packing constructor for n=26"""
171
+ centers, radii = construct_packing()
172
+ # Calculate the sum of radii
173
+ sum_radii = np.sum(radii)
174
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_100/original.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
4
+ and linear programming for optimal radii. This version incorporates refined
5
+ parameters for the hierarchical grid to maximize the sum of radii.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+ import math # Imported for trigonometric functions in central placement
11
+
12
+
13
+ def construct_packing():
14
+ """
15
+ Constructs a packing of 26 circles by performing a crossover between two
16
+ successful strategies. It uses a 5x5 grid base and incorporates a superior
17
+ central placement logic.
18
+
19
+ The base structure is a 5x5 grid with the center removed, from the "Current Program".
20
+ This provides a robust scaffold for 24 circles.
21
+
22
+ The crossover improvement comes from replacing the simple vertical split for the
23
+ two central circles with the parameterized asymmetric diagonal placement from the
24
+ "Inspiration Program", which is proven to yield a higher score.
25
+
26
+ Returns:
27
+ Tuple of (centers, radii)
28
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
29
+ radii: np.array of shape (26) with radius of each circle.
30
+ """
31
+ n = 26
32
+ centers = np.zeros((n, 2))
33
+ k = 0
34
+
35
+ # 1. Place 24 circles in a 5x5 grid, skipping the central point.
36
+ # This structure is inherited from the effective "Current Program".
37
+ num_grid_divs = 5
38
+ coords = np.linspace(0.1, 0.9, num_grid_divs)
39
+
40
+ for i in range(num_grid_divs):
41
+ for j in range(num_grid_divs):
42
+ # Skip the center of the 5x5 grid, which is at index (2, 2)
43
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
44
+ continue
45
+ centers[k] = [coords[i], coords[j]]
46
+ k += 1
47
+
48
+ # 2. Crossover: Place 2 circles in the central gap using the superior logic
49
+ # from the "Inspiration Program".
50
+ # Parameters are taken from the best-performing variant (score 2.51).
51
+ central_separation_distance = 0.107
52
+ central_asymmetry_angle_deg = 44.5
53
+
54
+ # R_prime is the distance from the square's center (0.5, 0.5) to each central circle's center.
55
+ R_prime = central_separation_distance / 2.0
56
+ angle_rad = math.radians(central_asymmetry_angle_deg)
57
+
58
+ # Calculate x and y offsets for the asymmetric diagonal placement.
59
+ dx = R_prime * math.cos(angle_rad)
60
+ dy = R_prime * math.sin(angle_rad)
61
+
62
+ center_point = 0.5
63
+ centers[k] = [center_point - dx, center_point - dy]
64
+ k += 1
65
+ centers[k] = [center_point + dx, center_point + dy]
66
+ k += 1
67
+
68
+
69
+ # Ensure centers are strictly within (0,1) to avoid numerical issues
70
+ # at boundaries when calculating radii.
71
+ centers = np.clip(centers, 1e-6, 1 - 1e-6)
72
+
73
+ # For the given centers, compute the radii that maximize the sum using LP.
74
+ radii = compute_max_radii(centers)
75
+
76
+ return centers, radii
77
+
78
+
79
+ def compute_max_radii(centers):
80
+ """
81
+ Computes the maximum possible radii for a given set of circle centers
82
+ by solving a linear programming problem. This maximizes the sum of radii
83
+ subject to non-overlapping and boundary constraints.
84
+ This function is retained from the best performing solution due to its
85
+ mathematical optimality for fixed centers.
86
+
87
+ Args:
88
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
89
+
90
+ Returns:
91
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
92
+ """
93
+ n = centers.shape[0]
94
+
95
+ # The objective is to maximize sum(radii), which is equivalent to
96
+ # minimizing sum(-radii).
97
+ c = -np.ones(n)
98
+
99
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
100
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
101
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
102
+ constraints = []
103
+ b_vector = []
104
+
105
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
106
+ # These ensure circles stay within the unit square.
107
+ for i in range(n):
108
+ # r_i <= x_i
109
+ row = np.zeros(n)
110
+ row[i] = 1
111
+ constraints.append(row)
112
+ b_vector.append(centers[i, 0])
113
+
114
+ # r_i <= 1 - x_i
115
+ row = np.zeros(n)
116
+ row[i] = 1
117
+ constraints.append(row)
118
+ b_vector.append(1 - centers[i, 0])
119
+
120
+ # r_i <= y_i
121
+ row = np.zeros(n)
122
+ row[i] = 1
123
+ constraints.append(row)
124
+ b_vector.append(centers[i, 1])
125
+
126
+ # r_i <= 1 - y_i
127
+ row = np.zeros(n)
128
+ row[i] = 1
129
+ constraints.append(row)
130
+ b_vector.append(1 - centers[i, 1])
131
+
132
+ # Pair constraints: r_i + r_j <= d_ij
133
+ # These prevent circles from overlapping.
134
+ for i in range(n):
135
+ for j in range(i + 1, n):
136
+ dist = np.linalg.norm(centers[i] - centers[j])
137
+ row = np.zeros(n)
138
+ row[i] = 1
139
+ row[j] = 1
140
+ constraints.append(row)
141
+ b_vector.append(dist)
142
+
143
+ A_ub = np.array(constraints)
144
+ b_ub = np.array(b_vector)
145
+
146
+ # All radii must be non-negative.
147
+ # The (0, None) bounds indicate r_i >= 0.
148
+ bounds = [(0, None) for _ in range(n)]
149
+
150
+ # Solve the linear program using the 'highs' solver for performance.
151
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
152
+
153
+ if res.success:
154
+ # Return the optimal radii found by the solver.
155
+ return res.x
156
+ else:
157
+ # Fallback in case of solver failure.
158
+ # Returning zeros implies no valid radii could be determined.
159
+ print(f"LP solver failed: {res.message}")
160
+ return np.zeros(n)
161
+ # EVOLVE-BLOCK-END
162
+
163
+
164
+ # This part remains fixed (not evolved)
165
+ def run_packing():
166
+ """Run the circle packing constructor for n=26"""
167
+ centers, radii = construct_packing()
168
+ # Calculate the sum of radii
169
+ sum_radii = np.sum(radii)
170
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_100/search_replace.txt ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <NAME>
2
+ non_uniform_grid_expansion
3
+ </NAME>
4
+ <DESCRIPTION>
5
+ This edit introduces a non-uniform grid structure to create more space in the center of the packing, a feature common in optimal solutions which rarely conform to perfect grids. I am modifying the grid definition from the uniform `np.linspace(0.1, 0.9, 5)` to a non-uniform `np.array([0.1, 0.28, 0.5, 0.72, 0.9])`. This change moves the inner grid lines at 0.3 and 0.7 outwards, effectively expanding the central void where the two extra circles are placed.
6
+
7
+ To capitalize on this newly created space, I am also increasing the separation distance of the two central circles from `0.107` to `0.120`. This allows them to move further apart and potentially grow larger.
8
+
9
+ This coordinated change represents a strategic trade-off: space is reduced for the 16 circles in the outer grid regions to provide more room for the 8 inner grid circles and the 2 central circles. The hypothesis is that the increase in radii for these 10 centrally located circles will more than compensate for the decrease in radii of the outer circles, leading to a net increase in the total sum of radii.
10
+ </DESCRIPTION>
11
+ <DIFF>
12
+ <<<<<<< SEARCH
13
+ # 1. Place 24 circles in a 5x5 grid, skipping the central point.
14
+ # This structure is inherited from the effective "Current Program".
15
+ num_grid_divs = 5
16
+ coords = np.linspace(0.1, 0.9, num_grid_divs)
17
+
18
+ for i in range(num_grid_divs):
19
+ for j in range(num_grid_divs):
20
+ # Skip the center of the 5x5 grid, which is at index (2, 2)
21
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
22
+ continue
23
+ centers[k] = [coords[i], coords[j]]
24
+ k += 1
25
+
26
+ # 2. Crossover: Place 2 circles in the central gap using the superior logic
27
+ # from the "Inspiration Program".
28
+ # Parameters are taken from the best-performing variant (score 2.51).
29
+ central_separation_distance = 0.107
30
+ central_asymmetry_angle_deg = 44.5
31
+ =======
32
+ # 1. Place 24 circles in a non-uniform 5x5 grid, skipping the central point.
33
+ # The grid is expanded in the center to create more space for the central
34
+ # circles and the circles on the medial axes. This is a trade-off, as it
35
+ # reduces space for circles in the outer grid regions.
36
+ num_grid_divs = 5
37
+ # Original coords: np.linspace(0.1, 0.9, 5) -> [0.1, 0.3, 0.5, 0.7, 0.9]
38
+ # We move the inner grid lines (0.3, 0.7) outwards to (0.28, 0.72).
39
+ coords = np.array([0.1, 0.28, 0.5, 0.72, 0.9])
40
+
41
+ for i in range(num_grid_divs):
42
+ for j in range(num_grid_divs):
43
+ # Skip the center of the 5x5 grid, which is at index (2, 2)
44
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
45
+ continue
46
+ centers[k] = [coords[i], coords[j]]
47
+ k += 1
48
+
49
+ # 2. Crossover: Place 2 circles in the now larger central gap.
50
+ # The separation distance is increased to utilize the extra space created by
51
+ # the non-uniform grid. The asymmetry angle is kept.
52
+ central_separation_distance = 0.120
53
+ central_asymmetry_angle_deg = 44.5
54
+ >>>>>>> REPLACE
55
+ </DIFF>
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_101/edit.diff ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,166 +1,164 @@
4
+ # EVOLVE-BLOCK-START
5
+ """
6
+ -Constructor-based circle packing for n=26 circles using a hierarchical grid
7
+ -and linear programming for optimal radii.
8
+ +Constructor-based circle packing for n=26 circles using an adaptive grid
9
+ +and linear programming for optimal radii. This version introduces a non-uniform
10
+ +grid to overcome the limitations of a rigid structure.
11
+ """
12
+
13
+ import numpy as np
14
+ from scipy.optimize import linprog
15
+ import math
16
+
17
+
18
+ def construct_packing():
19
+ """
20
+ - Constructs a packing of 26 circles based on a 5x5 grid with a modified center.
21
+ - The radii are then optimized using a linear programming solver to maximize their sum.
22
+ + Constructs a packing of 26 circles using an "adaptive grid relaxation" technique.
23
+ + This method modifies the standard 5x5 grid to create more space for the central circles,
24
+ + aiming to overcome the limitations of a rigid grid structure.
25
+
26
+ - This structure is based on a nearly optimal packing for 25 circles (a 5x5 grid)
27
+ - by removing the central circle and inserting two smaller circles in its place.
28
+ - This is a common motif in circle packing to increase density and is adopted from
29
+ - a previous higher-performing solution.
30
+ + The strategy is as follows:
31
+ + 1. Instead of a uniform `linspace(0.1, 0.9, 5)` grid, we construct a non-uniform
32
+ + grid. The outer grid lines are pulled slightly inward (margin from 0.1 to 0.095),
33
+ + and the inner grid lines are pushed slightly outward from the center (spacing from
34
+ + center changes from 0.2 to 0.205).
35
+ + 2. This "relaxation" creates a larger central area, reducing the geometric pressure on
36
+ + the two central circles, which are often the bottleneck in the packing.
37
+ + 3. The placement of the 24 grid circles follows this new adaptive grid.
38
+ + 4. The two central circles are placed using the asymmetric diagonal split configuration
39
+ + that proved most effective in previous high-scoring runs. This leverages past
40
+ + discoveries within the new, more flexible framework.
41
+ + 5. The final radii are optimized using linear programming, which can now exploit the
42
+ + additional space created in the center.
43
+
44
+ Returns:
45
+ Tuple of (centers, radii)
46
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
47
+ radii: np.array of shape (26) with radius of each circle.
48
+ """
49
+ n = 26
50
+ centers = np.zeros((n, 2))
51
+ + k = 0
52
+
53
+ - # 1. Place 24 circles in a 5x5 grid, skipping the central point.
54
+ - # The grid coordinates are chosen so the grid perfectly fits the unit square.
55
+ - # This corresponds to ideal radius of 0.1 for the 24 grid circles.
56
+ - num_grid_divs = 5
57
+ - coords = np.linspace(0.1, 0.9, num_grid_divs)
58
+ + # --- NOVEL PART: Adaptive Grid Relaxation ---
59
+ + # Define the non-uniform grid coordinates. This is the core innovation.
60
+ + # Pushing the inner grid outward creates more room for the central pair.
61
+ + # Pulling the outer grid inward is a trade-off to enable this.
62
+ + outer_margin = 0.095 # Original: 0.1. Pulled INWARD.
63
+ + inner_spacing_from_center = 0.205 # Original: 0.2. Pushed OUTWARD.
64
+
65
+ - k = 0
66
+ - for i in range(num_grid_divs):
67
+ - for j in range(num_grid_divs):
68
+ - # Skip the center of the 5x5 grid, which is at index (2, 2)
69
+ - if i == num_grid_divs // 2 and j == num_grid_divs // 2:
70
+ + coords = np.array([
71
+ + outer_margin,
72
+ + 0.5 - inner_spacing_from_center,
73
+ + 0.5,
74
+ + 0.5 + inner_spacing_from_center,
75
+ + 1.0 - outer_margin
76
+ + ])
77
+ +
78
+ + # 1. Place 24 circles on the new adaptive grid, skipping the central point.
79
+ + for i in range(5):
80
+ + for j in range(5):
81
+ + # Skip the center of the grid (index 2, 2)
82
+ + if i == 2 and j == 2:
83
+ continue
84
+ centers[k] = [coords[i], coords[j]]
85
+ k += 1
86
+
87
+ - # 2. Place 2 circles in the central gap created by removing the center grid circle.
88
+ - # Based on insights from higher-performing runs, an asymmetric diagonal split
89
+ - # with specific separation distance and angle provides better packing for the central circles.
90
+ - # The parameters are chosen to replicate a previously successful configuration.
91
+ - central_separation_distance = 0.107
92
+ - central_asymmetry_angle_deg = 44.5 # A slight perturbation from 45.0 degrees
93
+ + # 2. Place 2 circles in the central gap, using the best known parameters.
94
+ + # This configuration is retained from the highest-scoring previous attempts.
95
+ + central_separation_distance = 0.125
96
+ + central_asymmetry_angle_deg = 44.5
97
+ + pair_center_offset_x = -0.0015 # Small empirically-derived offset
98
+
99
+ - # R_prime is the distance from the center (0.5, 0.5) to one of the central circles.
100
+ - R_prime = central_separation_distance / 2.0
101
+ + angle_rad = np.deg2rad(central_asymmetry_angle_deg)
102
+ + half_sep = central_separation_distance / 2.0
103
+
104
+ - # Convert the asymmetry angle from degrees to radians.
105
+ - angle_rad = math.radians(central_asymmetry_angle_deg)
106
+ + # Calculate displacement vector for the pair
107
+ + delta = np.array([half_sep * np.cos(angle_rad), half_sep * np.sin(angle_rad)])
108
+
109
+ - # Calculate x and y offsets (dx, dy) from the center (0.5, 0.5)
110
+ - dx = R_prime * math.cos(angle_rad)
111
+ - dy = R_prime * math.sin(angle_rad)
112
+ + # Define the center of the pair with the offset
113
+ + center_point = np.array([0.5 + pair_center_offset_x, 0.5])
114
+
115
+ - center_point = 0.5
116
+ - centers[k] = [center_point - dx, center_point - dy]
117
+ + centers[k] = center_point - delta
118
+ k += 1
119
+ - centers[k] = [center_point + dx, center_point + dy]
120
+ + centers[k] = center_point + delta
121
+ k += 1
122
+
123
+ - # Clip centers to be strictly within the unit square to avoid numerical
124
+ - # issues with the LP solver at the boundaries. A smaller epsilon allows centers
125
+ - # to be placed even closer to the boundaries for marginal gains.
126
+ + # Clip centers to be strictly within (0,1) to avoid numerical issues.
127
+ centers = np.clip(centers, 1e-8, 1 - 1e-8)
128
+
129
+ - # For the given centers, compute the radii that maximize the sum.
130
+ + # For the given centers, compute the radii that maximize the sum using LP.
131
+ radii = compute_max_radii(centers)
132
+
133
+ return centers, radii
134
+
135
+
136
+ def compute_max_radii(centers):
137
+ """
138
+ Computes the maximum possible radii for a given set of circle centers
139
+ by solving a linear programming problem. This maximizes the sum of radii
140
+ - subject to non-overlapping and boundary constraints.
141
+ + subject to non-overlapping and boundary constraints. This function is
142
+ + retained for its proven optimality for a fixed set of centers.
143
+
144
+ Args:
145
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
146
+
147
+ Returns:
148
+ np.array of shape (n) with the optimal radius for each circle.
149
+ """
150
+ n = centers.shape[0]
151
+
152
+ # The objective is to maximize sum(radii), which is equivalent to
153
+ # minimizing sum(-radii).
154
+ c = -np.ones(n)
155
+
156
+ - # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
157
+ - # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
158
+ - # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
159
+ + # Inequality constraints (A_ub * r <= b_ub)
160
+ constraints = []
161
+ b_vector = []
162
+
163
+ - # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
164
+ + # Wall constraints: r_i <= dist_to_wall
165
+ for i in range(n):
166
+ - # r_i <= x_i
167
+ - row = np.zeros(n)
168
+ - row[i] = 1
169
+ - constraints.append(row)
170
+ - b_vector.append(centers[i, 0])
171
+ + x, y = centers[i]
172
+ + # r_i <= x
173
+ + row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(x)
174
+ + # r_i <= 1 - x
175
+ + row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(1 - x)
176
+ + # r_i <= y
177
+ + row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(y)
178
+ + # r_i <= 1 - y
179
+ + row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(1 - y)
180
+
181
+ - # r_i <= 1 - x_i
182
+ - row = np.zeros(n)
183
+ - row[i] = 1
184
+ - constraints.append(row)
185
+ - b_vector.append(1 - centers[i, 0])
186
+ -
187
+ - # r_i <= y_i
188
+ - row = np.zeros(n)
189
+ - row[i] = 1
190
+ - constraints.append(row)
191
+ - b_vector.append(centers[i, 1])
192
+ -
193
+ - # r_i <= 1 - y_i
194
+ - row = np.zeros(n)
195
+ - row[i] = 1
196
+ - constraints.append(row)
197
+ - b_vector.append(1 - centers[i, 1])
198
+ -
199
+ - # Pair constraints: r_i + r_j <= d_ij
200
+ + # Pair constraints: r_i + r_j <= dist(c_i, c_j)
201
+ for i in range(n):
202
+ for j in range(i + 1, n):
203
+ dist = np.linalg.norm(centers[i] - centers[j])
204
+ row = np.zeros(n)
205
+ row[i] = 1
206
+ row[j] = 1
207
+ constraints.append(row)
208
+ b_vector.append(dist)
209
+
210
+ A_ub = np.array(constraints)
211
+ b_ub = np.array(b_vector)
212
+
213
+ # All radii must be non-negative.
214
+ - bounds = [(0, None) for _ in range(n)]
215
+ + bounds = (0, None)
216
+
217
+ - # Solve the linear program. 'highs' is a fast and reliable solver.
218
+ + # Solve the linear program using the 'highs' solver for performance.
219
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
220
+
221
+ if res.success:
222
+ - # Return the optimal radii
223
+ return res.x
224
+ else:
225
+ - # Fallback in case of solver failure
226
+ - # An empty array will result in a sum of 0, signaling a problem.
227
+ + # Fallback in case of solver failure.
228
+ + print(f"LP solver failed: {res.message}")
229
+ return np.zeros(n)
230
+ -
231
+ -
232
+ # EVOLVE-BLOCK-END
233
+
234
+
235
+ # This part remains fixed (not evolved)
236
+ def run_packing():
237
+ """Run the circle packing constructor for n=26"""
238
+ centers, radii = construct_packing()
239
+ # Calculate the sum of radii
240
+ sum_radii = np.sum(radii)
241
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_101/main.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using an adaptive grid
4
+ and linear programming for optimal radii. This version introduces a non-uniform
5
+ grid to overcome the limitations of a rigid structure.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+ import math
11
+
12
+
13
+ def construct_packing():
14
+ """
15
+ Constructs a packing of 26 circles using an "adaptive grid relaxation" technique.
16
+ This method modifies the standard 5x5 grid to create more space for the central circles,
17
+ aiming to overcome the limitations of a rigid grid structure.
18
+
19
+ The strategy is as follows:
20
+ 1. Instead of a uniform `linspace(0.1, 0.9, 5)` grid, we construct a non-uniform
21
+ grid. The outer grid lines are pulled slightly inward (margin from 0.1 to 0.095),
22
+ and the inner grid lines are pushed slightly outward from the center (spacing from
23
+ center changes from 0.2 to 0.205).
24
+ 2. This "relaxation" creates a larger central area, reducing the geometric pressure on
25
+ the two central circles, which are often the bottleneck in the packing.
26
+ 3. The placement of the 24 grid circles follows this new adaptive grid.
27
+ 4. The two central circles are placed using the asymmetric diagonal split configuration
28
+ that proved most effective in previous high-scoring runs. This leverages past
29
+ discoveries within the new, more flexible framework.
30
+ 5. The final radii are optimized using linear programming, which can now exploit the
31
+ additional space created in the center.
32
+
33
+ Returns:
34
+ Tuple of (centers, radii)
35
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
36
+ radii: np.array of shape (26) with radius of each circle.
37
+ """
38
+ n = 26
39
+ centers = np.zeros((n, 2))
40
+ k = 0
41
+
42
+ # --- NOVEL PART: Adaptive Grid Relaxation ---
43
+ # Define the non-uniform grid coordinates. This is the core innovation.
44
+ # Pushing the inner grid outward creates more room for the central pair.
45
+ # Pulling the outer grid inward is a trade-off to enable this.
46
+ outer_margin = 0.095 # Original: 0.1. Pulled INWARD.
47
+ inner_spacing_from_center = 0.205 # Original: 0.2. Pushed OUTWARD.
48
+
49
+ coords = np.array([
50
+ outer_margin,
51
+ 0.5 - inner_spacing_from_center,
52
+ 0.5,
53
+ 0.5 + inner_spacing_from_center,
54
+ 1.0 - outer_margin
55
+ ])
56
+
57
+ # 1. Place 24 circles on the new adaptive grid, skipping the central point.
58
+ for i in range(5):
59
+ for j in range(5):
60
+ # Skip the center of the grid (index 2, 2)
61
+ if i == 2 and j == 2:
62
+ continue
63
+ centers[k] = [coords[i], coords[j]]
64
+ k += 1
65
+
66
+ # 2. Place 2 circles in the central gap, using the best known parameters.
67
+ # This configuration is retained from the highest-scoring previous attempts.
68
+ central_separation_distance = 0.125
69
+ central_asymmetry_angle_deg = 44.5
70
+ pair_center_offset_x = -0.0015 # Small empirically-derived offset
71
+
72
+ angle_rad = np.deg2rad(central_asymmetry_angle_deg)
73
+ half_sep = central_separation_distance / 2.0
74
+
75
+ # Calculate displacement vector for the pair
76
+ delta = np.array([half_sep * np.cos(angle_rad), half_sep * np.sin(angle_rad)])
77
+
78
+ # Define the center of the pair with the offset
79
+ center_point = np.array([0.5 + pair_center_offset_x, 0.5])
80
+
81
+ centers[k] = center_point - delta
82
+ k += 1
83
+ centers[k] = center_point + delta
84
+ k += 1
85
+
86
+ # Clip centers to be strictly within (0,1) to avoid numerical issues.
87
+ centers = np.clip(centers, 1e-8, 1 - 1e-8)
88
+
89
+ # For the given centers, compute the radii that maximize the sum using LP.
90
+ radii = compute_max_radii(centers)
91
+
92
+ return centers, radii
93
+
94
+
95
+ def compute_max_radii(centers):
96
+ """
97
+ Computes the maximum possible radii for a given set of circle centers
98
+ by solving a linear programming problem. This maximizes the sum of radii
99
+ subject to non-overlapping and boundary constraints. This function is
100
+ retained for its proven optimality for a fixed set of centers.
101
+
102
+ Args:
103
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
104
+
105
+ Returns:
106
+ np.array of shape (n) with the optimal radius for each circle.
107
+ """
108
+ n = centers.shape[0]
109
+
110
+ # The objective is to maximize sum(radii), which is equivalent to
111
+ # minimizing sum(-radii).
112
+ c = -np.ones(n)
113
+
114
+ # Inequality constraints (A_ub * r <= b_ub)
115
+ constraints = []
116
+ b_vector = []
117
+
118
+ # Wall constraints: r_i <= dist_to_wall
119
+ for i in range(n):
120
+ x, y = centers[i]
121
+ # r_i <= x
122
+ row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(x)
123
+ # r_i <= 1 - x
124
+ row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(1 - x)
125
+ # r_i <= y
126
+ row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(y)
127
+ # r_i <= 1 - y
128
+ row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(1 - y)
129
+
130
+ # Pair constraints: r_i + r_j <= dist(c_i, c_j)
131
+ for i in range(n):
132
+ for j in range(i + 1, n):
133
+ dist = np.linalg.norm(centers[i] - centers[j])
134
+ row = np.zeros(n)
135
+ row[i] = 1
136
+ row[j] = 1
137
+ constraints.append(row)
138
+ b_vector.append(dist)
139
+
140
+ A_ub = np.array(constraints)
141
+ b_ub = np.array(b_vector)
142
+
143
+ # All radii must be non-negative.
144
+ bounds = (0, None)
145
+
146
+ # Solve the linear program using the 'highs' solver for performance.
147
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
148
+
149
+ if res.success:
150
+ return res.x
151
+ else:
152
+ # Fallback in case of solver failure.
153
+ print(f"LP solver failed: {res.message}")
154
+ return np.zeros(n)
155
+ # EVOLVE-BLOCK-END
156
+
157
+
158
+ # This part remains fixed (not evolved)
159
+ def run_packing():
160
+ """Run the circle packing constructor for n=26"""
161
+ centers, radii = construct_packing()
162
+ # Calculate the sum of radii
163
+ sum_radii = np.sum(radii)
164
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_101/original.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
4
+ and linear programming for optimal radii.
5
+ """
6
+
7
+ import numpy as np
8
+ from scipy.optimize import linprog
9
+ import math
10
+
11
+
12
+ def construct_packing():
13
+ """
14
+ Constructs a packing of 26 circles based on a 5x5 grid with a modified center.
15
+ The radii are then optimized using a linear programming solver to maximize their sum.
16
+
17
+ This structure is based on a nearly optimal packing for 25 circles (a 5x5 grid)
18
+ by removing the central circle and inserting two smaller circles in its place.
19
+ This is a common motif in circle packing to increase density and is adopted from
20
+ a previous higher-performing solution.
21
+
22
+ Returns:
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
+ """
27
+ n = 26
28
+ centers = np.zeros((n, 2))
29
+
30
+ # 1. Place 24 circles in a 5x5 grid, skipping the central point.
31
+ # The grid coordinates are chosen so the grid perfectly fits the unit square.
32
+ # This corresponds to ideal radius of 0.1 for the 24 grid circles.
33
+ num_grid_divs = 5
34
+ coords = np.linspace(0.1, 0.9, num_grid_divs)
35
+
36
+ k = 0
37
+ for i in range(num_grid_divs):
38
+ for j in range(num_grid_divs):
39
+ # Skip the center of the 5x5 grid, which is at index (2, 2)
40
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
41
+ continue
42
+ centers[k] = [coords[i], coords[j]]
43
+ k += 1
44
+
45
+ # 2. Place 2 circles in the central gap created by removing the center grid circle.
46
+ # Based on insights from higher-performing runs, an asymmetric diagonal split
47
+ # with specific separation distance and angle provides better packing for the central circles.
48
+ # The parameters are chosen to replicate a previously successful configuration.
49
+ central_separation_distance = 0.107
50
+ central_asymmetry_angle_deg = 44.5 # A slight perturbation from 45.0 degrees
51
+
52
+ # R_prime is the distance from the center (0.5, 0.5) to one of the central circles.
53
+ R_prime = central_separation_distance / 2.0
54
+
55
+ # Convert the asymmetry angle from degrees to radians.
56
+ angle_rad = math.radians(central_asymmetry_angle_deg)
57
+
58
+ # Calculate x and y offsets (dx, dy) from the center (0.5, 0.5)
59
+ dx = R_prime * math.cos(angle_rad)
60
+ dy = R_prime * math.sin(angle_rad)
61
+
62
+ center_point = 0.5
63
+ centers[k] = [center_point - dx, center_point - dy]
64
+ k += 1
65
+ centers[k] = [center_point + dx, center_point + dy]
66
+ k += 1
67
+
68
+ # Clip centers to be strictly within the unit square to avoid numerical
69
+ # issues with the LP solver at the boundaries. A smaller epsilon allows centers
70
+ # to be placed even closer to the boundaries for marginal gains.
71
+ centers = np.clip(centers, 1e-8, 1 - 1e-8)
72
+
73
+ # For the given centers, compute the radii that maximize the sum.
74
+ radii = compute_max_radii(centers)
75
+
76
+ return centers, radii
77
+
78
+
79
+ def compute_max_radii(centers):
80
+ """
81
+ Computes the maximum possible radii for a given set of circle centers
82
+ by solving a linear programming problem. This maximizes the sum of radii
83
+ subject to non-overlapping and boundary constraints.
84
+
85
+ Args:
86
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
87
+
88
+ Returns:
89
+ np.array of shape (n) with the optimal radius for each circle.
90
+ """
91
+ n = centers.shape[0]
92
+
93
+ # The objective is to maximize sum(radii), which is equivalent to
94
+ # minimizing sum(-radii).
95
+ c = -np.ones(n)
96
+
97
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
98
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
99
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
100
+ constraints = []
101
+ b_vector = []
102
+
103
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
104
+ for i in range(n):
105
+ # r_i <= x_i
106
+ row = np.zeros(n)
107
+ row[i] = 1
108
+ constraints.append(row)
109
+ b_vector.append(centers[i, 0])
110
+
111
+ # r_i <= 1 - x_i
112
+ row = np.zeros(n)
113
+ row[i] = 1
114
+ constraints.append(row)
115
+ b_vector.append(1 - centers[i, 0])
116
+
117
+ # r_i <= y_i
118
+ row = np.zeros(n)
119
+ row[i] = 1
120
+ constraints.append(row)
121
+ b_vector.append(centers[i, 1])
122
+
123
+ # r_i <= 1 - y_i
124
+ row = np.zeros(n)
125
+ row[i] = 1
126
+ constraints.append(row)
127
+ b_vector.append(1 - centers[i, 1])
128
+
129
+ # Pair constraints: r_i + r_j <= d_ij
130
+ for i in range(n):
131
+ for j in range(i + 1, n):
132
+ dist = np.linalg.norm(centers[i] - centers[j])
133
+ row = np.zeros(n)
134
+ row[i] = 1
135
+ row[j] = 1
136
+ constraints.append(row)
137
+ b_vector.append(dist)
138
+
139
+ A_ub = np.array(constraints)
140
+ b_ub = np.array(b_vector)
141
+
142
+ # All radii must be non-negative.
143
+ bounds = [(0, None) for _ in range(n)]
144
+
145
+ # Solve the linear program. 'highs' is a fast and reliable solver.
146
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
147
+
148
+ if res.success:
149
+ # Return the optimal radii
150
+ return res.x
151
+ else:
152
+ # Fallback in case of solver failure
153
+ # An empty array will result in a sum of 0, signaling a problem.
154
+ return np.zeros(n)
155
+
156
+
157
+ # EVOLVE-BLOCK-END
158
+
159
+
160
+ # This part remains fixed (not evolved)
161
+ def run_packing():
162
+ """Run the circle packing constructor for n=26"""
163
+ centers, radii = construct_packing()
164
+ # Calculate the sum of radii
165
+ sum_radii = np.sum(radii)
166
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_101/rewrite.txt ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using an adaptive grid
4
+ and linear programming for optimal radii. This version introduces a non-uniform
5
+ grid to overcome the limitations of a rigid structure.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+ import math
11
+
12
+
13
+ def construct_packing():
14
+ """
15
+ Constructs a packing of 26 circles using an "adaptive grid relaxation" technique.
16
+ This method modifies the standard 5x5 grid to create more space for the central circles,
17
+ aiming to overcome the limitations of a rigid grid structure.
18
+
19
+ The strategy is as follows:
20
+ 1. Instead of a uniform `linspace(0.1, 0.9, 5)` grid, we construct a non-uniform
21
+ grid. The outer grid lines are pulled slightly inward (margin from 0.1 to 0.095),
22
+ and the inner grid lines are pushed slightly outward from the center (spacing from
23
+ center changes from 0.2 to 0.205).
24
+ 2. This "relaxation" creates a larger central area, reducing the geometric pressure on
25
+ the two central circles, which are often the bottleneck in the packing.
26
+ 3. The placement of the 24 grid circles follows this new adaptive grid.
27
+ 4. The two central circles are placed using the asymmetric diagonal split configuration
28
+ that proved most effective in previous high-scoring runs. This leverages past
29
+ discoveries within the new, more flexible framework.
30
+ 5. The final radii are optimized using linear programming, which can now exploit the
31
+ additional space created in the center.
32
+
33
+ Returns:
34
+ Tuple of (centers, radii)
35
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
36
+ radii: np.array of shape (26) with radius of each circle.
37
+ """
38
+ n = 26
39
+ centers = np.zeros((n, 2))
40
+ k = 0
41
+
42
+ # --- NOVEL PART: Adaptive Grid Relaxation ---
43
+ # Define the non-uniform grid coordinates. This is the core innovation.
44
+ # Pushing the inner grid outward creates more room for the central pair.
45
+ # Pulling the outer grid inward is a trade-off to enable this.
46
+ outer_margin = 0.095 # Original: 0.1. Pulled INWARD.
47
+ inner_spacing_from_center = 0.205 # Original: 0.2. Pushed OUTWARD.
48
+
49
+ coords = np.array([
50
+ outer_margin,
51
+ 0.5 - inner_spacing_from_center,
52
+ 0.5,
53
+ 0.5 + inner_spacing_from_center,
54
+ 1.0 - outer_margin
55
+ ])
56
+
57
+ # 1. Place 24 circles on the new adaptive grid, skipping the central point.
58
+ for i in range(5):
59
+ for j in range(5):
60
+ # Skip the center of the grid (index 2, 2)
61
+ if i == 2 and j == 2:
62
+ continue
63
+ centers[k] = [coords[i], coords[j]]
64
+ k += 1
65
+
66
+ # 2. Place 2 circles in the central gap, using the best known parameters.
67
+ # This configuration is retained from the highest-scoring previous attempts.
68
+ central_separation_distance = 0.125
69
+ central_asymmetry_angle_deg = 44.5
70
+ pair_center_offset_x = -0.0015 # Small empirically-derived offset
71
+
72
+ angle_rad = np.deg2rad(central_asymmetry_angle_deg)
73
+ half_sep = central_separation_distance / 2.0
74
+
75
+ # Calculate displacement vector for the pair
76
+ delta = np.array([half_sep * np.cos(angle_rad), half_sep * np.sin(angle_rad)])
77
+
78
+ # Define the center of the pair with the offset
79
+ center_point = np.array([0.5 + pair_center_offset_x, 0.5])
80
+
81
+ centers[k] = center_point - delta
82
+ k += 1
83
+ centers[k] = center_point + delta
84
+ k += 1
85
+
86
+ # Clip centers to be strictly within (0,1) to avoid numerical issues.
87
+ centers = np.clip(centers, 1e-8, 1 - 1e-8)
88
+
89
+ # For the given centers, compute the radii that maximize the sum using LP.
90
+ radii = compute_max_radii(centers)
91
+
92
+ return centers, radii
93
+
94
+
95
+ def compute_max_radii(centers):
96
+ """
97
+ Computes the maximum possible radii for a given set of circle centers
98
+ by solving a linear programming problem. This maximizes the sum of radii
99
+ subject to non-overlapping and boundary constraints. This function is
100
+ retained for its proven optimality for a fixed set of centers.
101
+
102
+ Args:
103
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
104
+
105
+ Returns:
106
+ np.array of shape (n) with the optimal radius for each circle.
107
+ """
108
+ n = centers.shape[0]
109
+
110
+ # The objective is to maximize sum(radii), which is equivalent to
111
+ # minimizing sum(-radii).
112
+ c = -np.ones(n)
113
+
114
+ # Inequality constraints (A_ub * r <= b_ub)
115
+ constraints = []
116
+ b_vector = []
117
+
118
+ # Wall constraints: r_i <= dist_to_wall
119
+ for i in range(n):
120
+ x, y = centers[i]
121
+ # r_i <= x
122
+ row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(x)
123
+ # r_i <= 1 - x
124
+ row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(1 - x)
125
+ # r_i <= y
126
+ row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(y)
127
+ # r_i <= 1 - y
128
+ row = np.zeros(n); row[i] = 1; constraints.append(row); b_vector.append(1 - y)
129
+
130
+ # Pair constraints: r_i + r_j <= dist(c_i, c_j)
131
+ for i in range(n):
132
+ for j in range(i + 1, n):
133
+ dist = np.linalg.norm(centers[i] - centers[j])
134
+ row = np.zeros(n)
135
+ row[i] = 1
136
+ row[j] = 1
137
+ constraints.append(row)
138
+ b_vector.append(dist)
139
+
140
+ A_ub = np.array(constraints)
141
+ b_ub = np.array(b_vector)
142
+
143
+ # All radii must be non-negative.
144
+ bounds = (0, None)
145
+
146
+ # Solve the linear program using the 'highs' solver for performance.
147
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
148
+
149
+ if res.success:
150
+ return res.x
151
+ else:
152
+ # Fallback in case of solver failure.
153
+ print(f"LP solver failed: {res.message}")
154
+ return np.zeros(n)
155
+ # EVOLVE-BLOCK-END
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/__pycache__/main.cpython-313.pyc ADDED
Binary file (5.35 kB). View file
 
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/edit.diff ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,161 +1,168 @@
4
+ # EVOLVE-BLOCK-START
5
+ """
6
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
7
+ and linear programming for optimal radii. This version incorporates refined
8
+ parameters for the hierarchical grid to maximize the sum of radii.
9
+ """
10
+
11
+ import numpy as np
12
+ from scipy.optimize import linprog
13
+
14
+
15
+ def construct_packing():
16
+ """
17
+ Constructs a packing of 26 circles based on a 5x5 grid with a modified center.
18
+ The radii are then optimized using a linear programming solver to maximize their sum.
19
+
20
+ This new structure moves away from the previous 4x4 hierarchical grid. It is
21
+ based on a nearly optimal packing for 25 circles (a 5x5 grid) by removing the
22
+ central circle and inserting two smaller circles in its place. This is a common
23
+ motif in circle packing to increase density.
24
+
25
+ Returns:
26
+ Tuple of (centers, 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
+ """
30
+ n = 26
31
+ centers = np.zeros((n, 2))
32
+ k = 0
33
+
34
+ # 1. Place 24 circles in a 5x5 grid, skipping the central point.
35
+ # The grid coordinates are chosen so the grid perfectly fits the unit square.
36
+ # This corresponds to ideal radius of 0.1 for the 24 grid circles.
37
+ num_grid_divs = 5
38
+ coords = np.linspace(0.1, 0.9, num_grid_divs)
39
+
40
+ for i in range(num_grid_divs):
41
+ for j in range(num_grid_divs):
42
+ # Skip the center of the 5x5 grid, which is at index (2, 2)
43
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
44
+ continue
45
+ centers[k] = [coords[i], coords[j]]
46
+ k += 1
47
+
48
+ - # 2. Place 2 circles in the central gap with a diagonal split.
49
+ - # This placement, with a separation of 0.107, is adopted from previous high-scoring
50
+ - # configurations. It breaks the axis-aligned symmetry of the vertical split,
51
+ - # creating a more balanced set of distances to the 8 surrounding grid circles.
52
+ - # This typically allows for a more efficient packing in the central region.
53
+ - separation = 0.107
54
+ - # Calculate the x and y offsets for a 45-degree diagonal placement.
55
+ - delta = separation / (2 * np.sqrt(2))
56
+ + # 2. Place 2 circles in the central gap using a parameterized asymmetric diagonal split.
57
+ + # This placement, with a separation of 0.107 and an angle of 44.5 degrees,
58
+ + # is adopted from previous high-scoring configurations. It breaks the axis-aligned
59
+ + # symmetry to create a more balanced set of distances to the 8 surrounding grid circles,
60
+ + # leading to more efficient packing in the central region.
61
+ + central_separation_distance = 0.107
62
+ + central_asymmetry_angle_deg = 44.5
63
+ +
64
+ + # R_prime is the distance from the square's center (0.5, 0.5) to each central circle's center.
65
+ + R_prime = central_separation_distance / 2.0
66
+ + angle_rad = math.radians(central_asymmetry_angle_deg)
67
+ +
68
+ + # Calculate x and y offsets (dx, dy) for the asymmetric diagonal placement.
69
+ + dx = R_prime * math.cos(angle_rad)
70
+ + dy = R_prime * math.sin(angle_rad)
71
+
72
+ center_point = 0.5
73
+ - centers[k] = [center_point - delta, center_point - delta]
74
+ + centers[k] = [center_point - dx, center_point - dy]
75
+ k += 1
76
+ - centers[k] = [center_point + delta, center_point + delta]
77
+ + centers[k] = [center_point + dx, center_point + dy]
78
+ k += 1
79
+
80
+ # Ensure centers are strictly within (0,1) to avoid numerical issues
81
+ # at boundaries when calculating radii.
82
+ centers = np.clip(centers, 1e-6, 1 - 1e-6)
83
+
84
+ # For the given centers, compute the radii that maximize the sum using LP.
85
+ radii = compute_max_radii(centers)
86
+
87
+ return centers, radii
88
+
89
+
90
+ def compute_max_radii(centers):
91
+ """
92
+ Computes the maximum possible radii for a given set of circle centers
93
+ by solving a linear programming problem. This maximizes the sum of radii
94
+ subject to non-overlapping and boundary constraints.
95
+ This function is retained from the best performing solution due to its
96
+ mathematical optimality for fixed centers.
97
+
98
+ Args:
99
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
100
+
101
+ Returns:
102
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
103
+ """
104
+ n = centers.shape[0]
105
+
106
+ # The objective is to maximize sum(radii), which is equivalent to
107
+ # minimizing sum(-radii).
108
+ c = -np.ones(n)
109
+
110
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
111
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
112
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
113
+ constraints = []
114
+ b_vector = []
115
+
116
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
117
+ # These ensure circles stay within the unit square.
118
+ for i in range(n):
119
+ # r_i <= x_i
120
+ row = np.zeros(n)
121
+ row[i] = 1
122
+ constraints.append(row)
123
+ b_vector.append(centers[i, 0])
124
+
125
+ # r_i <= 1 - x_i
126
+ row = np.zeros(n)
127
+ row[i] = 1
128
+ constraints.append(row)
129
+ b_vector.append(1 - centers[i, 0])
130
+
131
+ # r_i <= y_i
132
+ row = np.zeros(n)
133
+ row[i] = 1
134
+ constraints.append(row)
135
+ b_vector.append(centers[i, 1])
136
+
137
+ # r_i <= 1 - y_i
138
+ row = np.zeros(n)
139
+ row[i] = 1
140
+ constraints.append(row)
141
+ b_vector.append(1 - centers[i, 1])
142
+
143
+ # Pair constraints: r_i + r_j <= d_ij
144
+ # These prevent circles from overlapping.
145
+ for i in range(n):
146
+ for j in range(i + 1, n):
147
+ dist = np.linalg.norm(centers[i] - centers[j])
148
+ row = np.zeros(n)
149
+ row[i] = 1
150
+ row[j] = 1
151
+ constraints.append(row)
152
+ b_vector.append(dist)
153
+
154
+ A_ub = np.array(constraints)
155
+ b_ub = np.array(b_vector)
156
+
157
+ # All radii must be non-negative.
158
+ # The (0, None) bounds indicate r_i >= 0.
159
+ bounds = [(0, None) for _ in range(n)]
160
+
161
+ # Solve the linear program using the 'highs' solver for performance.
162
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
163
+
164
+ if res.success:
165
+ # Return the optimal radii found by the solver.
166
+ return res.x
167
+ else:
168
+ # Fallback in case of solver failure.
169
+ # Returning zeros implies no valid radii could be determined.
170
+ print(f"LP solver failed: {res.message}")
171
+ return np.zeros(n)
172
+ # EVOLVE-BLOCK-END
173
+
174
+
175
+ # This part remains fixed (not evolved)
176
+ def run_packing():
177
+ """Run the circle packing constructor for n=26"""
178
+ centers, radii = construct_packing()
179
+ # Calculate the sum of radii
180
+ sum_radii = np.sum(radii)
181
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/main.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
4
+ and linear programming for optimal radii. This version incorporates refined
5
+ parameters for the hierarchical grid to maximize the sum of radii.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+
11
+
12
+ def construct_packing():
13
+ """
14
+ Constructs a packing of 26 circles based on a 5x5 grid with a modified center.
15
+ The radii are then optimized using a linear programming solver to maximize their sum.
16
+
17
+ This new structure moves away from the previous 4x4 hierarchical grid. It is
18
+ based on a nearly optimal packing for 25 circles (a 5x5 grid) by removing the
19
+ central circle and inserting two smaller circles in its place. This is a common
20
+ motif in circle packing to increase density.
21
+
22
+ Returns:
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
+ """
27
+ n = 26
28
+ centers = np.zeros((n, 2))
29
+ k = 0
30
+
31
+ # 1. Place 24 circles in a 5x5 grid, skipping the central point.
32
+ # The grid coordinates are chosen so the grid perfectly fits the unit square.
33
+ # This corresponds to ideal radius of 0.1 for the 24 grid circles.
34
+ num_grid_divs = 5
35
+ coords = np.linspace(0.1, 0.9, num_grid_divs)
36
+
37
+ for i in range(num_grid_divs):
38
+ for j in range(num_grid_divs):
39
+ # Skip the center of the 5x5 grid, which is at index (2, 2)
40
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
41
+ continue
42
+ centers[k] = [coords[i], coords[j]]
43
+ k += 1
44
+
45
+ # 2. Place 2 circles in the central gap using a parameterized asymmetric diagonal split.
46
+ # This placement, with a separation of 0.107 and an angle of 44.5 degrees,
47
+ # is adopted from previous high-scoring configurations. It breaks the axis-aligned
48
+ # symmetry to create a more balanced set of distances to the 8 surrounding grid circles,
49
+ # leading to more efficient packing in the central region.
50
+ central_separation_distance = 0.107
51
+ central_asymmetry_angle_deg = 44.5
52
+
53
+ # R_prime is the distance from the square's center (0.5, 0.5) to each central circle's center.
54
+ R_prime = central_separation_distance / 2.0
55
+ angle_rad = math.radians(central_asymmetry_angle_deg)
56
+
57
+ # Calculate x and y offsets (dx, dy) for the asymmetric diagonal placement.
58
+ dx = R_prime * math.cos(angle_rad)
59
+ dy = R_prime * math.sin(angle_rad)
60
+
61
+ center_point = 0.5
62
+ centers[k] = [center_point - dx, center_point - dy]
63
+ k += 1
64
+ centers[k] = [center_point + dx, center_point + dy]
65
+ k += 1
66
+
67
+ # Ensure centers are strictly within (0,1) to avoid numerical issues
68
+ # at boundaries when calculating radii.
69
+ centers = np.clip(centers, 1e-6, 1 - 1e-6)
70
+
71
+ # For the given centers, compute the radii that maximize the sum using LP.
72
+ radii = compute_max_radii(centers)
73
+
74
+ return centers, radii
75
+
76
+
77
+ def compute_max_radii(centers):
78
+ """
79
+ Computes the maximum possible radii for a given set of circle centers
80
+ by solving a linear programming problem. This maximizes the sum of radii
81
+ subject to non-overlapping and boundary constraints.
82
+ This function is retained from the best performing solution due to its
83
+ mathematical optimality for fixed centers.
84
+
85
+ Args:
86
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
87
+
88
+ Returns:
89
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
90
+ """
91
+ n = centers.shape[0]
92
+
93
+ # The objective is to maximize sum(radii), which is equivalent to
94
+ # minimizing sum(-radii).
95
+ c = -np.ones(n)
96
+
97
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
98
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
99
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
100
+ constraints = []
101
+ b_vector = []
102
+
103
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
104
+ # These ensure circles stay within the unit square.
105
+ for i in range(n):
106
+ # r_i <= x_i
107
+ row = np.zeros(n)
108
+ row[i] = 1
109
+ constraints.append(row)
110
+ b_vector.append(centers[i, 0])
111
+
112
+ # r_i <= 1 - x_i
113
+ row = np.zeros(n)
114
+ row[i] = 1
115
+ constraints.append(row)
116
+ b_vector.append(1 - centers[i, 0])
117
+
118
+ # r_i <= y_i
119
+ row = np.zeros(n)
120
+ row[i] = 1
121
+ constraints.append(row)
122
+ b_vector.append(centers[i, 1])
123
+
124
+ # r_i <= 1 - y_i
125
+ row = np.zeros(n)
126
+ row[i] = 1
127
+ constraints.append(row)
128
+ b_vector.append(1 - centers[i, 1])
129
+
130
+ # Pair constraints: r_i + r_j <= d_ij
131
+ # These prevent circles from overlapping.
132
+ for i in range(n):
133
+ for j in range(i + 1, n):
134
+ dist = np.linalg.norm(centers[i] - centers[j])
135
+ row = np.zeros(n)
136
+ row[i] = 1
137
+ row[j] = 1
138
+ constraints.append(row)
139
+ b_vector.append(dist)
140
+
141
+ A_ub = np.array(constraints)
142
+ b_ub = np.array(b_vector)
143
+
144
+ # All radii must be non-negative.
145
+ # The (0, None) bounds indicate r_i >= 0.
146
+ bounds = [(0, None) for _ in range(n)]
147
+
148
+ # Solve the linear program using the 'highs' solver for performance.
149
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
150
+
151
+ if res.success:
152
+ # Return the optimal radii found by the solver.
153
+ return res.x
154
+ else:
155
+ # Fallback in case of solver failure.
156
+ # Returning zeros implies no valid radii could be determined.
157
+ print(f"LP solver failed: {res.message}")
158
+ return np.zeros(n)
159
+ # EVOLVE-BLOCK-END
160
+
161
+
162
+ # This part remains fixed (not evolved)
163
+ def run_packing():
164
+ """Run the circle packing constructor for n=26"""
165
+ centers, radii = construct_packing()
166
+ # Calculate the sum of radii
167
+ sum_radii = np.sum(radii)
168
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/original.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a hierarchical grid
4
+ and linear programming for optimal radii. This version incorporates refined
5
+ parameters for the hierarchical grid to maximize the sum of radii.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+
11
+
12
+ def construct_packing():
13
+ """
14
+ Constructs a packing of 26 circles based on a 5x5 grid with a modified center.
15
+ The radii are then optimized using a linear programming solver to maximize their sum.
16
+
17
+ This new structure moves away from the previous 4x4 hierarchical grid. It is
18
+ based on a nearly optimal packing for 25 circles (a 5x5 grid) by removing the
19
+ central circle and inserting two smaller circles in its place. This is a common
20
+ motif in circle packing to increase density.
21
+
22
+ Returns:
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
+ """
27
+ n = 26
28
+ centers = np.zeros((n, 2))
29
+ k = 0
30
+
31
+ # 1. Place 24 circles in a 5x5 grid, skipping the central point.
32
+ # The grid coordinates are chosen so the grid perfectly fits the unit square.
33
+ # This corresponds to ideal radius of 0.1 for the 24 grid circles.
34
+ num_grid_divs = 5
35
+ coords = np.linspace(0.1, 0.9, num_grid_divs)
36
+
37
+ for i in range(num_grid_divs):
38
+ for j in range(num_grid_divs):
39
+ # Skip the center of the 5x5 grid, which is at index (2, 2)
40
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
41
+ continue
42
+ centers[k] = [coords[i], coords[j]]
43
+ k += 1
44
+
45
+ # 2. Place 2 circles in the central gap with a diagonal split.
46
+ # This placement, with a separation of 0.107, is adopted from previous high-scoring
47
+ # configurations. It breaks the axis-aligned symmetry of the vertical split,
48
+ # creating a more balanced set of distances to the 8 surrounding grid circles.
49
+ # This typically allows for a more efficient packing in the central region.
50
+ separation = 0.107
51
+ # Calculate the x and y offsets for a 45-degree diagonal placement.
52
+ delta = separation / (2 * np.sqrt(2))
53
+
54
+ center_point = 0.5
55
+ centers[k] = [center_point - delta, center_point - delta]
56
+ k += 1
57
+ centers[k] = [center_point + delta, center_point + delta]
58
+ k += 1
59
+
60
+ # Ensure centers are strictly within (0,1) to avoid numerical issues
61
+ # at boundaries when calculating radii.
62
+ centers = np.clip(centers, 1e-6, 1 - 1e-6)
63
+
64
+ # For the given centers, compute the radii that maximize the sum using LP.
65
+ radii = compute_max_radii(centers)
66
+
67
+ return centers, radii
68
+
69
+
70
+ def compute_max_radii(centers):
71
+ """
72
+ Computes the maximum possible radii for a given set of circle centers
73
+ by solving a linear programming problem. This maximizes the sum of radii
74
+ subject to non-overlapping and boundary constraints.
75
+ This function is retained from the best performing solution due to its
76
+ mathematical optimality for fixed centers.
77
+
78
+ Args:
79
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
80
+
81
+ Returns:
82
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
83
+ """
84
+ n = centers.shape[0]
85
+
86
+ # The objective is to maximize sum(radii), which is equivalent to
87
+ # minimizing sum(-radii).
88
+ c = -np.ones(n)
89
+
90
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
91
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
92
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
93
+ constraints = []
94
+ b_vector = []
95
+
96
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
97
+ # These ensure circles stay within the unit square.
98
+ for i in range(n):
99
+ # r_i <= x_i
100
+ row = np.zeros(n)
101
+ row[i] = 1
102
+ constraints.append(row)
103
+ b_vector.append(centers[i, 0])
104
+
105
+ # r_i <= 1 - x_i
106
+ row = np.zeros(n)
107
+ row[i] = 1
108
+ constraints.append(row)
109
+ b_vector.append(1 - centers[i, 0])
110
+
111
+ # r_i <= y_i
112
+ row = np.zeros(n)
113
+ row[i] = 1
114
+ constraints.append(row)
115
+ b_vector.append(centers[i, 1])
116
+
117
+ # r_i <= 1 - y_i
118
+ row = np.zeros(n)
119
+ row[i] = 1
120
+ constraints.append(row)
121
+ b_vector.append(1 - centers[i, 1])
122
+
123
+ # Pair constraints: r_i + r_j <= d_ij
124
+ # These prevent circles from overlapping.
125
+ for i in range(n):
126
+ for j in range(i + 1, n):
127
+ dist = np.linalg.norm(centers[i] - centers[j])
128
+ row = np.zeros(n)
129
+ row[i] = 1
130
+ row[j] = 1
131
+ constraints.append(row)
132
+ b_vector.append(dist)
133
+
134
+ A_ub = np.array(constraints)
135
+ b_ub = np.array(b_vector)
136
+
137
+ # All radii must be non-negative.
138
+ # The (0, None) bounds indicate r_i >= 0.
139
+ bounds = [(0, None) for _ in range(n)]
140
+
141
+ # Solve the linear program using the 'highs' solver for performance.
142
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
143
+
144
+ if res.success:
145
+ # Return the optimal radii found by the solver.
146
+ return res.x
147
+ else:
148
+ # Fallback in case of solver failure.
149
+ # Returning zeros implies no valid radii could be determined.
150
+ print(f"LP solver failed: {res.message}")
151
+ return np.zeros(n)
152
+ # EVOLVE-BLOCK-END
153
+
154
+
155
+ # This part remains fixed (not evolved)
156
+ def run_packing():
157
+ """Run the circle packing constructor for n=26"""
158
+ centers, radii = construct_packing()
159
+ # Calculate the sum of radii
160
+ sum_radii = np.sum(radii)
161
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/results/correct.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "correct": false,
3
+ "error": "NameError: name 'math' is not defined"
4
+ }
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/results/metrics.json ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "combined_score": 0.0,
3
+ "correct": false,
4
+ "primary": {
5
+ "combined_score": 0.0,
6
+ "execution_time_mean": 0.0,
7
+ "execution_time_std": 0.0,
8
+ "num_successful_runs": 0,
9
+ "num_valid_runs": 0,
10
+ "num_invalid_runs": 0,
11
+ "all_validation_errors": [],
12
+ "correct": false,
13
+ "validation_error": "NameError: name 'math' is not defined"
14
+ },
15
+ "auxiliary": {
16
+ "max_circle_overlap_magnitude": 0.0,
17
+ "num_overlapping_pairs": 0,
18
+ "max_boundary_violation_magnitude": 0.0,
19
+ "num_boundary_violations": 0,
20
+ "violation_metrics_info": "No circles to check for violations.",
21
+ "total_area_covered": 0.0,
22
+ "packing_density": 0.0,
23
+ "empty_space_ratio": 1.0,
24
+ "area_metrics_info": "No radii data available to calculate area metrics.",
25
+ "avg_gap_between_circles": 0.0,
26
+ "min_gap_between_circles": 0.0,
27
+ "num_circles": 0,
28
+ "avg_radius": 0.0,
29
+ "std_dev_radius": 0.0,
30
+ "min_radius": 0.0,
31
+ "max_radius": 0.0,
32
+ "radii_coefficient_of_variation": 0.0,
33
+ "radius_stats_info": "No radii data available for radius statistics.",
34
+ "num_unique_radii": 0,
35
+ "diversity_metrics_info": "No radii data available for diversity metrics.",
36
+ "avg_distance_from_unit_center": 0.0,
37
+ "avg_distance_from_unit_center_normalized": 0.0,
38
+ "max_distance_from_unit_center": 0.0,
39
+ "center_x_std_dev": 0.0,
40
+ "center_y_std_dev": 0.0,
41
+ "positional_distribution_info": "No circles for positional distribution metrics.",
42
+ "avg_nearest_neighbor_distance_centers": 0.0,
43
+ "center_quadrant_density_variance": 0.0,
44
+ "spatial_metrics_info": "No circle centers for spatial metrics.",
45
+ "avg_min_distance_to_boundary": 0.0,
46
+ "min_overall_distance_to_boundary": 0.0,
47
+ "boundary_proximity_info": "No circles to calculate boundary proximity.",
48
+ "avg_pairwise_center_distance": 0.0,
49
+ "pairwise_center_distance_info": "Need at least 2 circles for pairwise distance.",
50
+ "normalized_score_per_circle": 0.0,
51
+ "packing_aspect_ratio_of_centers_bbox": 1.0,
52
+ "avg_num_touching_neighbors": 0.0,
53
+ "avg_quadrant_radii_std_dev": 0.0,
54
+ "advanced_spatial_metrics_info": "No circles for advanced spatial metrics.",
55
+ "primary_combined_score": 0.0
56
+ },
57
+ "auxiliary_descriptions": {
58
+ "total_area_covered": "Sum of the areas of all 26 circles (pi * r^2 for each). This measures how much of the unit square is occupied by the circles. A higher value indicates better space utilization, focusing on the actual \"filled\" area rather than just the sum of radii (which is the primary score). Range 0 to ~1 (or slightly more if circles could overlap, but primary prevents that).",
59
+ "packing_density": "This is identical to `total_area_covered` because the unit square has an area of 1. It directly represents the percentage of the available area covered by circles. Higher is better, indicating more efficient use of space.",
60
+ "avg_min_distance_to_boundary": "The average of the minimum distances from the edge of each circle to any of the four boundaries of the unit square. Smaller values (closer to 0) indicate that circles are packed more tightly against the boundaries, which is often a characteristic of optimal packing solutions. Negative values would imply a violation (circle outside boundary) which the primary metric already checks. Useful for understanding how well the solution leverages the container edges.",
61
+ "min_overall_distance_to_boundary": "The single smallest distance from any circle's edge to any boundary. This metric highlights if at least one circle is positioned extremely close to a boundary, indicating a strategy to maximize space.",
62
+ "avg_radius": "The average radius of the 26 circles. This gives an idea of the typical size of circles in the packing. Can indicate if solutions are favoring uniform or varied circle sizes.",
63
+ "std_dev_radius": "The standard deviation of the radii of the 26 circles. A higher standard deviation indicates greater diversity in circle sizes, while a lower one suggests more uniform sizes. This can reveal different packing strategies and encourage diversity.",
64
+ "min_radius": "The smallest radius among the 26 circles. Can indicate if the solution is introducing very small circles.",
65
+ "max_radius": "The largest radius among the 26 circles. Can indicate if the solution is trying to fit very large circles.",
66
+ "avg_nearest_neighbor_distance_centers": "This metric calculates the average Euclidean distance from each circle's center to the center of its closest neighboring circle. A lower value indicates a more tightly packed or clustered arrangement of circle centers, while a higher value suggests a more dispersed or sparse distribution. This is useful for identifying if the packing strategy is creating dense clusters or leaving significant gaps between groups of circles.",
67
+ "center_quadrant_density_variance": "This metric divides the unit square into four equal quadrants (2x2 grid) and computes the variance of the number of circle centers found within each quadrant. A lower variance indicates a more uniform distribution of circle centers across the entire packing area, suggesting a balanced utilization of space. A higher variance implies that circle centers are concentrated in certain quadrants, potentially indicating inefficient or uneven packing.",
68
+ "empty_space_ratio": "A decreasing trend in this metric, even with a stable `sum(radii)`, indicates the solution is becoming more efficient in terms of area utilization. If it's stagnant, it suggests wasted space.",
69
+ "avg_gap_between_circles": "The average positive distance between the edges of any two non-overlapping circles. A smaller average gap suggests a tighter packing configuration where circles are placed closer to each other without violating overlap constraints.",
70
+ "min_gap_between_circles": "The smallest positive distance between the edges of any two non-overlapping circles. A value near zero indicates that at least one pair of circles is almost touching, which is characteristic of optimal packing where circles are tightly constrained.",
71
+ "radii_coefficient_of_variation": "Changes in this metric can reveal if the solution is exploring new combinations of circle sizes. A sudden increase might suggest an attempt to break free from a local optimum by introducing more diverse sizes, while a decrease might indicate convergence towards a more uniform or specific size distribution.",
72
+ "avg_distance_from_unit_center": "The average Euclidean distance of all circle centers from the center of the unit square (0.5, 0.5). A lower value suggests circles are clustered more towards the center of the packing area, while a higher value indicates a more peripheral or spread-out distribution.",
73
+ "max_distance_from_unit_center": "The maximum Euclidean distance of any circle center from the center of the unit square (0.5, 0.5). This indicates how far out the outermost circle is placed, providing insight into the spread of the packing.",
74
+ "center_x_std_dev": "The standard deviation of the x-coordinates of all circle centers. A lower value indicates less spread horizontally.",
75
+ "center_y_std_dev": "The standard deviation of the y-coordinates of all circle centers. A lower value indicates less spread vertically. Together with `center_x_std_dev`, these metrics describe the spatial distribution and clustering of circles.",
76
+ "packing_aspect_ratio_of_centers_bbox": "Monitoring this metric can reveal if the packing is becoming more balanced (closer to 1.0) or if it's developing a preferred elongation. A deviation from 1.0 might suggest that the algorithm is struggling to utilize the square boundaries effectively, or it could be an emergent property of a specific packing strategy. If the primary score plateaus, but this metric moves closer to 1.0, it could indicate structural refinement.",
77
+ "avg_num_touching_neighbors": "A sustained increase in this metric, especially if the primary score is stagnant, would be a strong indicator of improved packing density and efficiency. It suggests that circles are being fit together more closely. If this metric is low and stagnating, it implies that there are significant gaps between circles that could be exploited for larger radii.",
78
+ "avg_quadrant_radii_std_dev": "This metric provides spatial insight into radii diversity. If this value is high, it means different regions of the packing are employing varied circle sizes. If it's low, it indicates a more uniform sizing strategy across the board. This can inform whether the evolution should be encouraged to explore more diverse sizes regionally or to converge on a more uniform size distribution if that proves more efficient for the overall sum of radii.",
79
+ "avg_distance_from_unit_center_normalized": "The average distance of all circle centers from the center of the unit square (0.5, 0.5), normalized by the maximum possible distance from the center (`sqrt(0.5)`). A value closer to 0 indicates a more central packing, while a value closer to 1 implies circles are pushed towards the corners or edges, utilizing the full extent of the square. Useful for assessing if the packing strategy prioritizes central arrangements or boundary-conforming ones.",
80
+ "avg_pairwise_center_distance": "The average Euclidean distance between all unique pairs of circle centers. A higher value suggests a more spread-out arrangement of circles, potentially indicating a less \"clustered\" or more uniform distribution of centers. This complements nearest-neighbor distance by considering all pairs and provides a global measure of center dispersion."
81
+ },
82
+ "timestamp": 1770497647.551893,
83
+ "generation": 102
84
+ }
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_102/search_replace.txt ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <NAME>
2
+ central_angle_tune
3
+ </NAME>
4
+
5
+ <DESCRIPTION>
6
+ Reintroducing the `central_asymmetry_angle_deg` parameter from previous high-performing solutions. The current program hardcodes a 45-degree diagonal split for the two central circles. By replacing this with a parameterized angle, set to `44.5` degrees, we allow for a slight perturbation from perfect symmetry. This perturbation was previously observed to yield a slightly better packing by optimizing the central circle positions relative to the surrounding 24 grid circles, thus maximizing their collective radii.
7
+ </DESCRIPTION>
8
+
9
+ <DIFF>
10
+ <<<<<<< SEARCH
11
+ # 2. Place 2 circles in the central gap with a diagonal split.
12
+ # This placement, with a separation of 0.107, is adopted from previous high-scoring
13
+ # configurations. It breaks the axis-aligned symmetry of the vertical split,
14
+ # creating a more balanced set of distances to the 8 surrounding grid circles.
15
+ # This typically allows for a more efficient packing in the central region.
16
+ separation = 0.107
17
+ # Calculate the x and y offsets for a 45-degree diagonal placement.
18
+ delta = separation / (2 * np.sqrt(2))
19
+
20
+ center_point = 0.5
21
+ centers[k] = [center_point - delta, center_point - delta]
22
+ k += 1
23
+ centers[k] = [center_point + delta, center_point + delta]
24
+ k += 1
25
+ =======
26
+ # 2. Place 2 circles in the central gap using a parameterized asymmetric diagonal split.
27
+ # This placement, with a separation of 0.107 and an angle of 44.5 degrees,
28
+ # is adopted from previous high-scoring configurations. It breaks the axis-aligned
29
+ # symmetry to create a more balanced set of distances to the 8 surrounding grid circles,
30
+ # leading to more efficient packing in the central region.
31
+ central_separation_distance = 0.107
32
+ central_asymmetry_angle_deg = 44.5
33
+
34
+ # R_prime is the distance from the square's center (0.5, 0.5) to each central circle's center.
35
+ R_prime = central_separation_distance / 2.0
36
+ angle_rad = math.radians(central_asymmetry_angle_deg)
37
+
38
+ # Calculate x and y offsets (dx, dy) for the asymmetric diagonal placement.
39
+ dx = R_prime * math.cos(angle_rad)
40
+ dy = R_prime * math.sin(angle_rad)
41
+
42
+ center_point = 0.5
43
+ centers[k] = [center_point - dx, center_point - dy]
44
+ k += 1
45
+ centers[k] = [center_point + dx, center_point + dy]
46
+ k += 1
47
+ >>>>>>> REPLACE
48
+ </DIFF>
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_103/edit.diff ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,189 +1,196 @@
4
+ # EVOLVE-BLOCK-START
5
+ """
6
+ Constructor-based circle packing for n=26 circles using a modified 5x5 grid
7
+ and linear programming for optimal radii. This version refines the central
8
+ circle placement based on geometric constraint analysis.
9
+ """
10
+
11
+ import numpy as np
12
+ from scipy.optimize import linprog
13
+ import math # Added for math.radians, math.cos, math.sin
14
+
15
+
16
+ def construct_packing():
17
+ """
18
+ Constructs a packing of 26 circles based on a 5x5 grid. The central two
19
+ circles are placed using a configuration derived from balancing geometric
20
+ constraints to maximize packing potential.
21
+
22
+ The strategy is based on the following analysis:
23
+ 1. The `(5x5-1)` grid structure with `linspace(0.1, 0.9, 5)` is retained
24
+ as a proven high-performance base.
25
+ 2. The key improvement is in placing the two central circles. Their radii are
26
+ limited by their distance to each other and to the 8 surrounding grid
27
+ circles. An optimal placement should balance these distances.
28
+ 3. A previous symmetric configuration (angle=45 deg) showed that a
29
+ `central_separation_distance` of `0.1552` perfectly balanced the
30
+ distance between the central pair and their nearest grid neighbors.
31
+ 4. However, the highest scores were achieved with a slight asymmetry
32
+ (angle=44.5 deg), which likely prevents geometric locking.
33
+ 5. This version combines the best of both worlds: the geometrically optimal
34
+ separation distance (`0.1552`) with the performance-enhancing
35
+ asymmetric angle (`44.5` deg). The small pair offset from the previous
36
+ version is removed (set to 0) as it did not improve the score and
37
+ complicates the core geometry.
38
+
39
+ Returns:
40
+ Tuple of (centers, radii)
41
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
42
+ radii: np.array of shape (26) with radius of each circle.
43
+ """
44
+ n = 26
45
+ centers = np.zeros((n, 2))
46
+ k = 0
47
+
48
+ # 1. Place 24 circles in a 5x5 grid, skipping the central point.
49
+ # This regular grid provides a strong, stable base structure.
50
+ num_grid_divs = 5
51
+ coords = np.linspace(0.1, 0.9, num_grid_divs)
52
+
53
+ for i in range(num_grid_divs):
54
+ for j in range(num_grid_divs):
55
+ # Skip the center of the 5x5 grid (index 2, 2) to make space.
56
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
57
+ continue
58
+ centers[k] = [coords[i], coords[j]]
59
+ k += 1
60
+
61
+ - # 2. Place 2 circles in the central gap using a geometrically balanced and
62
+ - # asymmetric configuration.
63
+ - central_separation_distance = 0.1552 # Optimal distance for constraint balancing
64
+ - central_pair_orientation_angle_deg = 44.5 # Proven asymmetry to break geometric locking
65
+ + # 2. Place 2 circles in the central gap using an empirically tuned configuration with a new perturbation.
66
+ + # Analysis of prior runs shows the highest score (2.52) was achieved with a
67
+ + # smaller separation distance (0.125), an asymmetric angle, and a slight horizontal offset.
68
+ + # This configuration reverts to those high-performing parameters and introduces a
69
+ + # new vertical offset to further break symmetry and seek a better optimum.
70
+
71
+ - # Offsets for the center of the central pair are set to zero to focus on
72
+ - # the primary parameters of separation and orientation.
73
+ - central_pair_offset_x = 0.0
74
+ - central_pair_offset_y = 0.0
75
+ + # Revert to the empirically superior separation distance. The value 0.125
76
+ + # outperformed the "geometrically balanced" 0.1552.
77
+ + central_separation_distance = 0.125
78
+ + central_pair_orientation_angle_deg = 44.5 # Proven asymmetry from high-scoring runs.
79
+ +
80
+ + # Re-introduce the horizontal offset and add a new vertical offset. This shifts
81
+ + # the central pair's centroid slightly up and to the left, creating a more
82
+ + # complex and potentially advantageous pressure dynamic on surrounding grid circles.
83
+ + central_pair_offset_x = -0.0015
84
+ + central_pair_offset_y = 0.0010
85
+
86
+ # R_prime is half the distance between the two central centers.
87
+ R_prime = central_separation_distance / 2.0
88
+ angle_rad = math.radians(central_pair_orientation_angle_deg)
89
+
90
+ # Calculate internal offsets (dx_internal, dy_internal) for the two circles relative to their pair's center.
91
+ dx_internal = R_prime * math.cos(angle_rad)
92
+ dy_internal = R_prime * math.sin(angle_rad)
93
+
94
+ # Calculate the actual center point of the pair
95
+ center_pair_x = 0.5 + central_pair_offset_x
96
+ center_pair_y = 0.5 + central_pair_offset_y
97
+
98
+ # Place the two central circles around their calculated pair center.
99
+ centers[k] = [center_pair_x - dx_internal, center_pair_y - dy_internal]
100
+ k += 1
101
+ centers[k] = [center_pair_x + dx_internal, center_pair_y + dy_internal]
102
+ k += 1
103
+
104
+ # Clip centers to be strictly within (0,1) to avoid numerical issues
105
+ # at boundaries when calculating radii. A smaller epsilon (1e-8) allows centers
106
+ # to be placed even closer to the boundaries for marginal gains, especially
107
+ # for grid circles intended to touch the walls (e.g., at 0.1 or 0.9).
108
+ centers = np.clip(centers, 1e-8, 1 - 1e-8)
109
+
110
+ # For the given centers, compute the radii that maximize the sum using LP.
111
+ radii = compute_max_radii(centers)
112
+
113
+ return centers, radii
114
+
115
+
116
+ def compute_max_radii(centers):
117
+ """
118
+ Computes the maximum possible radii for a given set of circle centers
119
+ by solving a linear programming problem. This maximizes the sum of radii
120
+ subject to non-overlapping and boundary constraints.
121
+ This function is retained from the best performing solution due to its
122
+ mathematical optimality for fixed centers.
123
+
124
+ Args:
125
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
126
+
127
+ Returns:
128
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
129
+ """
130
+ n = centers.shape[0]
131
+
132
+ # The objective is to maximize sum(radii), which is equivalent to
133
+ # minimizing sum(-radii).
134
+ c = -np.ones(n)
135
+
136
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
137
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
138
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
139
+ constraints = []
140
+ b_vector = []
141
+
142
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
143
+ # These ensure circles stay within the unit square.
144
+ for i in range(n):
145
+ # r_i <= x_i
146
+ row = np.zeros(n)
147
+ row[i] = 1
148
+ constraints.append(row)
149
+ b_vector.append(centers[i, 0])
150
+
151
+ # r_i <= 1 - x_i
152
+ row = np.zeros(n)
153
+ row[i] = 1
154
+ constraints.append(row)
155
+ b_vector.append(1 - centers[i, 0])
156
+
157
+ # r_i <= y_i
158
+ row = np.zeros(n)
159
+ row[i] = 1
160
+ constraints.append(row)
161
+ b_vector.append(centers[i, 1])
162
+
163
+ # r_i <= 1 - y_i
164
+ row = np.zeros(n)
165
+ row[i] = 1
166
+ constraints.append(row)
167
+ b_vector.append(1 - centers[i, 1])
168
+
169
+ # Pair constraints: r_i + r_j <= d_ij
170
+ # These prevent circles from overlapping.
171
+ for i in range(n):
172
+ for j in range(i + 1, n):
173
+ dist = np.linalg.norm(centers[i] - centers[j])
174
+ row = np.zeros(n)
175
+ row[i] = 1
176
+ row[j] = 1
177
+ constraints.append(row)
178
+ b_vector.append(dist)
179
+
180
+ A_ub = np.array(constraints)
181
+ b_ub = np.array(b_vector)
182
+
183
+ # All radii must be non-negative.
184
+ # The (0, None) bounds indicate r_i >= 0.
185
+ bounds = [(0, None) for _ in range(n)]
186
+
187
+ # Solve the linear program using the 'highs' solver for performance.
188
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
189
+
190
+ if res.success:
191
+ # Return the optimal radii found by the solver.
192
+ return res.x
193
+ else:
194
+ # Fallback in case of solver failure.
195
+ # Returning zeros implies no valid radii could be determined.
196
+ print(f"LP solver failed: {res.message}")
197
+ return np.zeros(n)
198
+ # EVOLVE-BLOCK-END
199
+
200
+
201
+ # This part remains fixed (not evolved)
202
+ def run_packing():
203
+ """Run the circle packing constructor for n=26"""
204
+ centers, radii = construct_packing()
205
+ # Calculate the sum of radii
206
+ sum_radii = np.sum(radii)
207
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_103/main.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a modified 5x5 grid
4
+ and linear programming for optimal radii. This version refines the central
5
+ circle placement based on geometric constraint analysis.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+ import math # Added for math.radians, math.cos, math.sin
11
+
12
+
13
+ def construct_packing():
14
+ """
15
+ Constructs a packing of 26 circles based on a 5x5 grid. The central two
16
+ circles are placed using a configuration derived from balancing geometric
17
+ constraints to maximize packing potential.
18
+
19
+ The strategy is based on the following analysis:
20
+ 1. The `(5x5-1)` grid structure with `linspace(0.1, 0.9, 5)` is retained
21
+ as a proven high-performance base.
22
+ 2. The key improvement is in placing the two central circles. Their radii are
23
+ limited by their distance to each other and to the 8 surrounding grid
24
+ circles. An optimal placement should balance these distances.
25
+ 3. A previous symmetric configuration (angle=45 deg) showed that a
26
+ `central_separation_distance` of `0.1552` perfectly balanced the
27
+ distance between the central pair and their nearest grid neighbors.
28
+ 4. However, the highest scores were achieved with a slight asymmetry
29
+ (angle=44.5 deg), which likely prevents geometric locking.
30
+ 5. This version combines the best of both worlds: the geometrically optimal
31
+ separation distance (`0.1552`) with the performance-enhancing
32
+ asymmetric angle (`44.5` deg). The small pair offset from the previous
33
+ version is removed (set to 0) as it did not improve the score and
34
+ complicates the core geometry.
35
+
36
+ Returns:
37
+ Tuple of (centers, radii)
38
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
39
+ radii: np.array of shape (26) with radius of each circle.
40
+ """
41
+ n = 26
42
+ centers = np.zeros((n, 2))
43
+ k = 0
44
+
45
+ # 1. Place 24 circles in a 5x5 grid, skipping the central point.
46
+ # This regular grid provides a strong, stable base structure.
47
+ num_grid_divs = 5
48
+ coords = np.linspace(0.1, 0.9, num_grid_divs)
49
+
50
+ for i in range(num_grid_divs):
51
+ for j in range(num_grid_divs):
52
+ # Skip the center of the 5x5 grid (index 2, 2) to make space.
53
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
54
+ continue
55
+ centers[k] = [coords[i], coords[j]]
56
+ k += 1
57
+
58
+ # 2. Place 2 circles in the central gap using an empirically tuned configuration with a new perturbation.
59
+ # Analysis of prior runs shows the highest score (2.52) was achieved with a
60
+ # smaller separation distance (0.125), an asymmetric angle, and a slight horizontal offset.
61
+ # This configuration reverts to those high-performing parameters and introduces a
62
+ # new vertical offset to further break symmetry and seek a better optimum.
63
+
64
+ # Revert to the empirically superior separation distance. The value 0.125
65
+ # outperformed the "geometrically balanced" 0.1552.
66
+ central_separation_distance = 0.125
67
+ central_pair_orientation_angle_deg = 44.5 # Proven asymmetry from high-scoring runs.
68
+
69
+ # Re-introduce the horizontal offset and add a new vertical offset. This shifts
70
+ # the central pair's centroid slightly up and to the left, creating a more
71
+ # complex and potentially advantageous pressure dynamic on surrounding grid circles.
72
+ central_pair_offset_x = -0.0015
73
+ central_pair_offset_y = 0.0010
74
+
75
+ # R_prime is half the distance between the two central centers.
76
+ R_prime = central_separation_distance / 2.0
77
+ angle_rad = math.radians(central_pair_orientation_angle_deg)
78
+
79
+ # Calculate internal offsets (dx_internal, dy_internal) for the two circles relative to their pair's center.
80
+ dx_internal = R_prime * math.cos(angle_rad)
81
+ dy_internal = R_prime * math.sin(angle_rad)
82
+
83
+ # Calculate the actual center point of the pair
84
+ center_pair_x = 0.5 + central_pair_offset_x
85
+ center_pair_y = 0.5 + central_pair_offset_y
86
+
87
+ # Place the two central circles around their calculated pair center.
88
+ centers[k] = [center_pair_x - dx_internal, center_pair_y - dy_internal]
89
+ k += 1
90
+ centers[k] = [center_pair_x + dx_internal, center_pair_y + dy_internal]
91
+ k += 1
92
+
93
+ # Clip centers to be strictly within (0,1) to avoid numerical issues
94
+ # at boundaries when calculating radii. A smaller epsilon (1e-8) allows centers
95
+ # to be placed even closer to the boundaries for marginal gains, especially
96
+ # for grid circles intended to touch the walls (e.g., at 0.1 or 0.9).
97
+ centers = np.clip(centers, 1e-8, 1 - 1e-8)
98
+
99
+ # For the given centers, compute the radii that maximize the sum using LP.
100
+ radii = compute_max_radii(centers)
101
+
102
+ return centers, radii
103
+
104
+
105
+ def compute_max_radii(centers):
106
+ """
107
+ Computes the maximum possible radii for a given set of circle centers
108
+ by solving a linear programming problem. This maximizes the sum of radii
109
+ subject to non-overlapping and boundary constraints.
110
+ This function is retained from the best performing solution due to its
111
+ mathematical optimality for fixed centers.
112
+
113
+ Args:
114
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
115
+
116
+ Returns:
117
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
118
+ """
119
+ n = centers.shape[0]
120
+
121
+ # The objective is to maximize sum(radii), which is equivalent to
122
+ # minimizing sum(-radii).
123
+ c = -np.ones(n)
124
+
125
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
126
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
127
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
128
+ constraints = []
129
+ b_vector = []
130
+
131
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
132
+ # These ensure circles stay within the unit square.
133
+ for i in range(n):
134
+ # r_i <= x_i
135
+ row = np.zeros(n)
136
+ row[i] = 1
137
+ constraints.append(row)
138
+ b_vector.append(centers[i, 0])
139
+
140
+ # r_i <= 1 - x_i
141
+ row = np.zeros(n)
142
+ row[i] = 1
143
+ constraints.append(row)
144
+ b_vector.append(1 - centers[i, 0])
145
+
146
+ # r_i <= y_i
147
+ row = np.zeros(n)
148
+ row[i] = 1
149
+ constraints.append(row)
150
+ b_vector.append(centers[i, 1])
151
+
152
+ # r_i <= 1 - y_i
153
+ row = np.zeros(n)
154
+ row[i] = 1
155
+ constraints.append(row)
156
+ b_vector.append(1 - centers[i, 1])
157
+
158
+ # Pair constraints: r_i + r_j <= d_ij
159
+ # These prevent circles from overlapping.
160
+ for i in range(n):
161
+ for j in range(i + 1, n):
162
+ dist = np.linalg.norm(centers[i] - centers[j])
163
+ row = np.zeros(n)
164
+ row[i] = 1
165
+ row[j] = 1
166
+ constraints.append(row)
167
+ b_vector.append(dist)
168
+
169
+ A_ub = np.array(constraints)
170
+ b_ub = np.array(b_vector)
171
+
172
+ # All radii must be non-negative.
173
+ # The (0, None) bounds indicate r_i >= 0.
174
+ bounds = [(0, None) for _ in range(n)]
175
+
176
+ # Solve the linear program using the 'highs' solver for performance.
177
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
178
+
179
+ if res.success:
180
+ # Return the optimal radii found by the solver.
181
+ return res.x
182
+ else:
183
+ # Fallback in case of solver failure.
184
+ # Returning zeros implies no valid radii could be determined.
185
+ print(f"LP solver failed: {res.message}")
186
+ return np.zeros(n)
187
+ # EVOLVE-BLOCK-END
188
+
189
+
190
+ # This part remains fixed (not evolved)
191
+ def run_packing():
192
+ """Run the circle packing constructor for n=26"""
193
+ centers, radii = construct_packing()
194
+ # Calculate the sum of radii
195
+ sum_radii = np.sum(radii)
196
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_103/original.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a modified 5x5 grid
4
+ and linear programming for optimal radii. This version refines the central
5
+ circle placement based on geometric constraint analysis.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+ import math # Added for math.radians, math.cos, math.sin
11
+
12
+
13
+ def construct_packing():
14
+ """
15
+ Constructs a packing of 26 circles based on a 5x5 grid. The central two
16
+ circles are placed using a configuration derived from balancing geometric
17
+ constraints to maximize packing potential.
18
+
19
+ The strategy is based on the following analysis:
20
+ 1. The `(5x5-1)` grid structure with `linspace(0.1, 0.9, 5)` is retained
21
+ as a proven high-performance base.
22
+ 2. The key improvement is in placing the two central circles. Their radii are
23
+ limited by their distance to each other and to the 8 surrounding grid
24
+ circles. An optimal placement should balance these distances.
25
+ 3. A previous symmetric configuration (angle=45 deg) showed that a
26
+ `central_separation_distance` of `0.1552` perfectly balanced the
27
+ distance between the central pair and their nearest grid neighbors.
28
+ 4. However, the highest scores were achieved with a slight asymmetry
29
+ (angle=44.5 deg), which likely prevents geometric locking.
30
+ 5. This version combines the best of both worlds: the geometrically optimal
31
+ separation distance (`0.1552`) with the performance-enhancing
32
+ asymmetric angle (`44.5` deg). The small pair offset from the previous
33
+ version is removed (set to 0) as it did not improve the score and
34
+ complicates the core geometry.
35
+
36
+ Returns:
37
+ Tuple of (centers, radii)
38
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
39
+ radii: np.array of shape (26) with radius of each circle.
40
+ """
41
+ n = 26
42
+ centers = np.zeros((n, 2))
43
+ k = 0
44
+
45
+ # 1. Place 24 circles in a 5x5 grid, skipping the central point.
46
+ # This regular grid provides a strong, stable base structure.
47
+ num_grid_divs = 5
48
+ coords = np.linspace(0.1, 0.9, num_grid_divs)
49
+
50
+ for i in range(num_grid_divs):
51
+ for j in range(num_grid_divs):
52
+ # Skip the center of the 5x5 grid (index 2, 2) to make space.
53
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
54
+ continue
55
+ centers[k] = [coords[i], coords[j]]
56
+ k += 1
57
+
58
+ # 2. Place 2 circles in the central gap using a geometrically balanced and
59
+ # asymmetric configuration.
60
+ central_separation_distance = 0.1552 # Optimal distance for constraint balancing
61
+ central_pair_orientation_angle_deg = 44.5 # Proven asymmetry to break geometric locking
62
+
63
+ # Offsets for the center of the central pair are set to zero to focus on
64
+ # the primary parameters of separation and orientation.
65
+ central_pair_offset_x = 0.0
66
+ central_pair_offset_y = 0.0
67
+
68
+ # R_prime is half the distance between the two central centers.
69
+ R_prime = central_separation_distance / 2.0
70
+ angle_rad = math.radians(central_pair_orientation_angle_deg)
71
+
72
+ # Calculate internal offsets (dx_internal, dy_internal) for the two circles relative to their pair's center.
73
+ dx_internal = R_prime * math.cos(angle_rad)
74
+ dy_internal = R_prime * math.sin(angle_rad)
75
+
76
+ # Calculate the actual center point of the pair
77
+ center_pair_x = 0.5 + central_pair_offset_x
78
+ center_pair_y = 0.5 + central_pair_offset_y
79
+
80
+ # Place the two central circles around their calculated pair center.
81
+ centers[k] = [center_pair_x - dx_internal, center_pair_y - dy_internal]
82
+ k += 1
83
+ centers[k] = [center_pair_x + dx_internal, center_pair_y + dy_internal]
84
+ k += 1
85
+
86
+ # Clip centers to be strictly within (0,1) to avoid numerical issues
87
+ # at boundaries when calculating radii. A smaller epsilon (1e-8) allows centers
88
+ # to be placed even closer to the boundaries for marginal gains, especially
89
+ # for grid circles intended to touch the walls (e.g., at 0.1 or 0.9).
90
+ centers = np.clip(centers, 1e-8, 1 - 1e-8)
91
+
92
+ # For the given centers, compute the radii that maximize the sum using LP.
93
+ radii = compute_max_radii(centers)
94
+
95
+ return centers, radii
96
+
97
+
98
+ def compute_max_radii(centers):
99
+ """
100
+ Computes the maximum possible radii for a given set of circle centers
101
+ by solving a linear programming problem. This maximizes the sum of radii
102
+ subject to non-overlapping and boundary constraints.
103
+ This function is retained from the best performing solution due to its
104
+ mathematical optimality for fixed centers.
105
+
106
+ Args:
107
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
108
+
109
+ Returns:
110
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
111
+ """
112
+ n = centers.shape[0]
113
+
114
+ # The objective is to maximize sum(radii), which is equivalent to
115
+ # minimizing sum(-radii).
116
+ c = -np.ones(n)
117
+
118
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
119
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
120
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
121
+ constraints = []
122
+ b_vector = []
123
+
124
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
125
+ # These ensure circles stay within the unit square.
126
+ for i in range(n):
127
+ # r_i <= x_i
128
+ row = np.zeros(n)
129
+ row[i] = 1
130
+ constraints.append(row)
131
+ b_vector.append(centers[i, 0])
132
+
133
+ # r_i <= 1 - x_i
134
+ row = np.zeros(n)
135
+ row[i] = 1
136
+ constraints.append(row)
137
+ b_vector.append(1 - centers[i, 0])
138
+
139
+ # r_i <= y_i
140
+ row = np.zeros(n)
141
+ row[i] = 1
142
+ constraints.append(row)
143
+ b_vector.append(centers[i, 1])
144
+
145
+ # r_i <= 1 - y_i
146
+ row = np.zeros(n)
147
+ row[i] = 1
148
+ constraints.append(row)
149
+ b_vector.append(1 - centers[i, 1])
150
+
151
+ # Pair constraints: r_i + r_j <= d_ij
152
+ # These prevent circles from overlapping.
153
+ for i in range(n):
154
+ for j in range(i + 1, n):
155
+ dist = np.linalg.norm(centers[i] - centers[j])
156
+ row = np.zeros(n)
157
+ row[i] = 1
158
+ row[j] = 1
159
+ constraints.append(row)
160
+ b_vector.append(dist)
161
+
162
+ A_ub = np.array(constraints)
163
+ b_ub = np.array(b_vector)
164
+
165
+ # All radii must be non-negative.
166
+ # The (0, None) bounds indicate r_i >= 0.
167
+ bounds = [(0, None) for _ in range(n)]
168
+
169
+ # Solve the linear program using the 'highs' solver for performance.
170
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
171
+
172
+ if res.success:
173
+ # Return the optimal radii found by the solver.
174
+ return res.x
175
+ else:
176
+ # Fallback in case of solver failure.
177
+ # Returning zeros implies no valid radii could be determined.
178
+ print(f"LP solver failed: {res.message}")
179
+ return np.zeros(n)
180
+ # EVOLVE-BLOCK-END
181
+
182
+
183
+ # This part remains fixed (not evolved)
184
+ def run_packing():
185
+ """Run the circle packing constructor for n=26"""
186
+ centers, radii = construct_packing()
187
+ # Calculate the sum of radii
188
+ sum_radii = np.sum(radii)
189
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_103/search_replace.txt ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Based on my analysis of the previous runs, the key to improving the sum of radii lies in the precise placement of the two central circles. The data clearly shows that a configuration with a separation distance of `0.125` and a slight asymmetry (`angle=44.5`, `offset_x=-0.0015`) achieved the peak score of 2.52, outperforming the current "geometrically balanced" approach which scores 2.51.
2
+
3
+ My proposed change is to revert to these empirically superior parameters and introduce a novel perturbation. By adding a small vertical offset (`central_pair_offset_y`) to the central pair's centroid, we break the last remaining axial symmetry. This shifts the pair's center of mass diagonally, creating a more complex pressure dynamic on the 8 surrounding grid circles. This should prevent any residual geometric locking and allow the linear programming solver to explore a new, potentially more efficient packing configuration that was previously inaccessible.
4
+
5
+ <NAME>
6
+ revert_to_empirical_params_with_y_offset
7
+ </NAME>
8
+
9
+ <DESCRIPTION>
10
+ This edit reverts the central circle configuration to the empirically superior parameters from the highest-scoring previous run and introduces a new perturbation.
11
+
12
+ 1. **`central_separation_distance`**: Changed from `0.1552` back to `0.125`. The performance data shows that the smaller separation distance yielded a better score (2.52 vs 2.51), indicating that a tighter constraint between the central pair is more beneficial than balancing constraints with the grid circles.
13
+ 2. **`central_pair_offset_x`**: Changed from `0.0` to `-0.0015`. This re-introduces the slight horizontal offset that was present in the top-performing configuration, which helps break symmetry.
14
+ 3. **`central_pair_offset_y`**: Changed from `0.0` to `0.0010`. This introduces a new vertical offset, creating a diagonal shift (`-x`, `+y`) of the central pair's centroid. This novel perturbation breaks the final axis of symmetry, aiming to unlock a more globally optimal packing by altering the pressure on the surrounding grid circles in a more complex and advantageous way.
15
+ </DESCRIPTION>
16
+
17
+ <DIFF>
18
+ <<<<<<< SEARCH
19
+ # 2. Place 2 circles in the central gap using a geometrically balanced and
20
+ # asymmetric configuration.
21
+ central_separation_distance = 0.1552 # Optimal distance for constraint balancing
22
+ central_pair_orientation_angle_deg = 44.5 # Proven asymmetry to break geometric locking
23
+
24
+ # Offsets for the center of the central pair are set to zero to focus on
25
+ # the primary parameters of separation and orientation.
26
+ central_pair_offset_x = 0.0
27
+ central_pair_offset_y = 0.0
28
+ =======
29
+ # 2. Place 2 circles in the central gap using an empirically tuned configuration with a new perturbation.
30
+ # Analysis of prior runs shows the highest score (2.52) was achieved with a
31
+ # smaller separation distance (0.125), an asymmetric angle, and a slight horizontal offset.
32
+ # This configuration reverts to those high-performing parameters and introduces a
33
+ # new vertical offset to further break symmetry and seek a better optimum.
34
+
35
+ # Revert to the empirically superior separation distance. The value 0.125
36
+ # outperformed the "geometrically balanced" 0.1552.
37
+ central_separation_distance = 0.125
38
+ central_pair_orientation_angle_deg = 44.5 # Proven asymmetry from high-scoring runs.
39
+
40
+ # Re-introduce the horizontal offset and add a new vertical offset. This shifts
41
+ # the central pair's centroid slightly up and to the left, creating a more
42
+ # complex and potentially advantageous pressure dynamic on surrounding grid circles.
43
+ central_pair_offset_x = -0.0015
44
+ central_pair_offset_y = 0.0010
45
+ >>>>>>> REPLACE
46
+ </DIFF>
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/__pycache__/main.cpython-313.pyc ADDED
Binary file (5.66 kB). View file
 
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/edit.diff ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,174 +1,180 @@
4
+ # EVOLVE-BLOCK-START
5
+ """
6
+ Constructor-based circle packing for n=26 circles using a modified 5x5 grid
7
+ and linear programming for optimal radii. This version introduces a diagonal
8
+ split for the central circles as a crossover from previous designs.
9
+ """
10
+
11
+ import numpy as np
12
+ from scipy.optimize import linprog
13
+ import math # Added for math.radians, math.cos, math.sin
14
+
15
+
16
+ def construct_packing():
17
+ """
18
+ Constructs a packing of 26 circles based on a 5x5 grid, with the two central
19
+ circles placed diagonally to break symmetry and improve packing efficiency.
20
+ The radii are then optimized using a linear programming solver to maximize their sum.
21
+
22
+ This structure is a crossover between two approaches:
23
+ 1. It uses the high-performing 5x5 grid (24 circles) from the inspiration script.
24
+ 2. It modifies the central placement by rotating the two inner circles by 45 degrees.
25
+ This is a reasoned perturbation aimed at improving upon the previous best score.
26
+
27
+ Returns:
28
+ Tuple of (centers, radii)
29
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
30
+ radii: np.array of shape (26) with radius of each circle.
31
+ """
32
+ n = 26
33
+ centers = np.zeros((n, 2))
34
+ k = 0
35
+
36
+ - # 1. Place 24 circles in a 5x5 grid, skipping the central point.
37
+ - # This regular grid provides a strong, stable base structure.
38
+ + # 1. Place 24 circles in a non-uniform 5x5 grid, skipping the central point.
39
+ + # The grid is modified to give more space to the central region. By pushing the
40
+ + # inner grid lines (originally at 0.3 and 0.7) slightly outwards to 0.29 and 0.71,
41
+ + # we increase the size of the central cell. This should relax constraints on the
42
+ + # two central circles, allowing for larger radii.
43
+ num_grid_divs = 5
44
+ - coords = np.linspace(0.1, 0.9, num_grid_divs)
45
+ + inner_grid_offset = 0.01
46
+ + coords = np.array([0.1, 0.3 - inner_grid_offset, 0.5, 0.7 + inner_grid_offset, 0.9])
47
+
48
+ for i in range(num_grid_divs):
49
+ for j in range(num_grid_divs):
50
+ # Skip the center of the 5x5 grid (index 2, 2) to make space.
51
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
52
+ continue
53
+ centers[k] = [coords[i], coords[j]]
54
+ k += 1
55
+
56
+ - # 2. Place 2 circles in the central gap with a perfectly symmetric diagonal split.
57
+ - # Analysis of previous runs shows that balancing the constraints on the central circles is key.
58
+ - # The two main constraints are the distance to the other central circle (d_cc) and
59
+ - # the distance to the nearest grid neighbors (d_cg).
60
+ - # An analytical model predicts that these two distances are equalized when the
61
+ - # separation is ~0.1552 and the angle is 45 degrees. This configuration
62
+ - # should remove local bottlenecks and allow the LP solver to find a more
63
+ - # globally optimal solution, leading to a higher sum of radii.
64
+ - central_separation_distance = 0.1552
65
+ - central_asymmetry_angle_deg = 45.0 # Use perfect 45-degree symmetry for the balanced configuration
66
+ + # 2. Place 2 circles in the central gap, using empirically superior parameters.
67
+ + # Previous runs have consistently shown that a separation of 0.125 and a slight
68
+ + # asymmetry angle of 44.5 degrees yield a higher score (2.52) than the analytically
69
+ + # "balanced" but symmetric configuration (2.51). This change reinstates these
70
+ + # high-performing values and re-introduces a small horizontal offset to the pair's
71
+ + # centroid, which was part of the best-known setup and breaks further symmetry.
72
+ + central_separation_distance = 0.125
73
+ + central_asymmetry_angle_deg = 44.5
74
+ + central_pair_offset_x = -0.0015
75
+
76
+ # R_prime is half the distance between the two central centers.
77
+ R_prime = central_separation_distance / 2.0
78
+ angle_rad = math.radians(central_asymmetry_angle_deg)
79
+
80
+ - # Calculate offsets (dx, dy) from the square's center (0.5, 0.5).
81
+ + # Calculate internal offsets (dx, dy) relative to the pair's center.
82
+ dx = R_prime * math.cos(angle_rad)
83
+ dy = R_prime * math.sin(angle_rad)
84
+
85
+ - center_point = 0.5
86
+ - centers[k] = [center_point - dx, center_point - dy]
87
+ + # The center of the pair is shifted slightly off (0.5, 0.5).
88
+ + center_pair_x = 0.5 + central_pair_offset_x
89
+ + center_pair_y = 0.5
90
+ +
91
+ + centers[k] = [center_pair_x - dx, center_pair_y - dy]
92
+ k += 1
93
+ - centers[k] = [center_point + dx, center_point + dy]
94
+ + centers[k] = [center_pair_x + dx, center_pair_y + dy]
95
+ k += 1
96
+
97
+ # Clip centers to be strictly within (0,1) to avoid numerical issues
98
+ # at boundaries when calculating radii. A smaller epsilon (1e-8) allows centers
99
+ # to be placed even closer to the boundaries for marginal gains, especially
100
+ # for grid circles intended to touch the walls (e.g., at 0.1 or 0.9).
101
+ centers = np.clip(centers, 1e-8, 1 - 1e-8)
102
+
103
+ # For the given centers, compute the radii that maximize the sum using LP.
104
+ radii = compute_max_radii(centers)
105
+
106
+ return centers, radii
107
+
108
+
109
+ def compute_max_radii(centers):
110
+ """
111
+ Computes the maximum possible radii for a given set of circle centers
112
+ by solving a linear programming problem. This maximizes the sum of radii
113
+ subject to non-overlapping and boundary constraints.
114
+ This function is retained from the best performing solution due to its
115
+ mathematical optimality for fixed centers.
116
+
117
+ Args:
118
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
119
+
120
+ Returns:
121
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
122
+ """
123
+ n = centers.shape[0]
124
+
125
+ # The objective is to maximize sum(radii), which is equivalent to
126
+ # minimizing sum(-radii).
127
+ c = -np.ones(n)
128
+
129
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
130
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
131
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
132
+ constraints = []
133
+ b_vector = []
134
+
135
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
136
+ # These ensure circles stay within the unit square.
137
+ for i in range(n):
138
+ # r_i <= x_i
139
+ row = np.zeros(n)
140
+ row[i] = 1
141
+ constraints.append(row)
142
+ b_vector.append(centers[i, 0])
143
+
144
+ # r_i <= 1 - x_i
145
+ row = np.zeros(n)
146
+ row[i] = 1
147
+ constraints.append(row)
148
+ b_vector.append(1 - centers[i, 0])
149
+
150
+ # r_i <= y_i
151
+ row = np.zeros(n)
152
+ row[i] = 1
153
+ constraints.append(row)
154
+ b_vector.append(centers[i, 1])
155
+
156
+ # r_i <= 1 - y_i
157
+ row = np.zeros(n)
158
+ row[i] = 1
159
+ constraints.append(row)
160
+ b_vector.append(1 - centers[i, 1])
161
+
162
+ # Pair constraints: r_i + r_j <= d_ij
163
+ # These prevent circles from overlapping.
164
+ for i in range(n):
165
+ for j in range(i + 1, n):
166
+ dist = np.linalg.norm(centers[i] - centers[j])
167
+ row = np.zeros(n)
168
+ row[i] = 1
169
+ row[j] = 1
170
+ constraints.append(row)
171
+ b_vector.append(dist)
172
+
173
+ A_ub = np.array(constraints)
174
+ b_ub = np.array(b_vector)
175
+
176
+ # All radii must be non-negative.
177
+ # The (0, None) bounds indicate r_i >= 0.
178
+ bounds = [(0, None) for _ in range(n)]
179
+
180
+ # Solve the linear program using the 'highs' solver for performance.
181
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
182
+
183
+ if res.success:
184
+ # Return the optimal radii found by the solver.
185
+ return res.x
186
+ else:
187
+ # Fallback in case of solver failure.
188
+ # Returning zeros implies no valid radii could be determined.
189
+ print(f"LP solver failed: {res.message}")
190
+ return np.zeros(n)
191
+ # EVOLVE-BLOCK-END
192
+
193
+
194
+ # This part remains fixed (not evolved)
195
+ def run_packing():
196
+ """Run the circle packing constructor for n=26"""
197
+ centers, radii = construct_packing()
198
+ # Calculate the sum of radii
199
+ sum_radii = np.sum(radii)
200
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/main.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a modified 5x5 grid
4
+ and linear programming for optimal radii. This version introduces a diagonal
5
+ split for the central circles as a crossover from previous designs.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+ import math # Added for math.radians, math.cos, math.sin
11
+
12
+
13
+ def construct_packing():
14
+ """
15
+ Constructs a packing of 26 circles based on a 5x5 grid, with the two central
16
+ circles placed diagonally to break symmetry and improve packing efficiency.
17
+ The radii are then optimized using a linear programming solver to maximize their sum.
18
+
19
+ This structure is a crossover between two approaches:
20
+ 1. It uses the high-performing 5x5 grid (24 circles) from the inspiration script.
21
+ 2. It modifies the central placement by rotating the two inner circles by 45 degrees.
22
+ This is a reasoned perturbation aimed at improving upon the previous best score.
23
+
24
+ Returns:
25
+ Tuple of (centers, radii)
26
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
27
+ radii: np.array of shape (26) with radius of each circle.
28
+ """
29
+ n = 26
30
+ centers = np.zeros((n, 2))
31
+ k = 0
32
+
33
+ # 1. Place 24 circles in a non-uniform 5x5 grid, skipping the central point.
34
+ # The grid is modified to give more space to the central region. By pushing the
35
+ # inner grid lines (originally at 0.3 and 0.7) slightly outwards to 0.29 and 0.71,
36
+ # we increase the size of the central cell. This should relax constraints on the
37
+ # two central circles, allowing for larger radii.
38
+ num_grid_divs = 5
39
+ inner_grid_offset = 0.01
40
+ coords = np.array([0.1, 0.3 - inner_grid_offset, 0.5, 0.7 + inner_grid_offset, 0.9])
41
+
42
+ for i in range(num_grid_divs):
43
+ for j in range(num_grid_divs):
44
+ # Skip the center of the 5x5 grid (index 2, 2) to make space.
45
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
46
+ continue
47
+ centers[k] = [coords[i], coords[j]]
48
+ k += 1
49
+
50
+ # 2. Place 2 circles in the central gap, using empirically superior parameters.
51
+ # Previous runs have consistently shown that a separation of 0.125 and a slight
52
+ # asymmetry angle of 44.5 degrees yield a higher score (2.52) than the analytically
53
+ # "balanced" but symmetric configuration (2.51). This change reinstates these
54
+ # high-performing values and re-introduces a small horizontal offset to the pair's
55
+ # centroid, which was part of the best-known setup and breaks further symmetry.
56
+ central_separation_distance = 0.125
57
+ central_asymmetry_angle_deg = 44.5
58
+ central_pair_offset_x = -0.0015
59
+
60
+ # R_prime is half the distance between the two central centers.
61
+ R_prime = central_separation_distance / 2.0
62
+ angle_rad = math.radians(central_asymmetry_angle_deg)
63
+
64
+ # Calculate internal offsets (dx, dy) relative to the pair's center.
65
+ dx = R_prime * math.cos(angle_rad)
66
+ dy = R_prime * math.sin(angle_rad)
67
+
68
+ # The center of the pair is shifted slightly off (0.5, 0.5).
69
+ center_pair_x = 0.5 + central_pair_offset_x
70
+ center_pair_y = 0.5
71
+
72
+ centers[k] = [center_pair_x - dx, center_pair_y - dy]
73
+ k += 1
74
+ centers[k] = [center_pair_x + dx, center_pair_y + dy]
75
+ k += 1
76
+
77
+ # Clip centers to be strictly within (0,1) to avoid numerical issues
78
+ # at boundaries when calculating radii. A smaller epsilon (1e-8) allows centers
79
+ # to be placed even closer to the boundaries for marginal gains, especially
80
+ # for grid circles intended to touch the walls (e.g., at 0.1 or 0.9).
81
+ centers = np.clip(centers, 1e-8, 1 - 1e-8)
82
+
83
+ # For the given centers, compute the radii that maximize the sum using LP.
84
+ radii = compute_max_radii(centers)
85
+
86
+ return centers, radii
87
+
88
+
89
+ def compute_max_radii(centers):
90
+ """
91
+ Computes the maximum possible radii for a given set of circle centers
92
+ by solving a linear programming problem. This maximizes the sum of radii
93
+ subject to non-overlapping and boundary constraints.
94
+ This function is retained from the best performing solution due to its
95
+ mathematical optimality for fixed centers.
96
+
97
+ Args:
98
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
99
+
100
+ Returns:
101
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
102
+ """
103
+ n = centers.shape[0]
104
+
105
+ # The objective is to maximize sum(radii), which is equivalent to
106
+ # minimizing sum(-radii).
107
+ c = -np.ones(n)
108
+
109
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
110
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
111
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
112
+ constraints = []
113
+ b_vector = []
114
+
115
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
116
+ # These ensure circles stay within the unit square.
117
+ for i in range(n):
118
+ # r_i <= x_i
119
+ row = np.zeros(n)
120
+ row[i] = 1
121
+ constraints.append(row)
122
+ b_vector.append(centers[i, 0])
123
+
124
+ # r_i <= 1 - x_i
125
+ row = np.zeros(n)
126
+ row[i] = 1
127
+ constraints.append(row)
128
+ b_vector.append(1 - centers[i, 0])
129
+
130
+ # r_i <= y_i
131
+ row = np.zeros(n)
132
+ row[i] = 1
133
+ constraints.append(row)
134
+ b_vector.append(centers[i, 1])
135
+
136
+ # r_i <= 1 - y_i
137
+ row = np.zeros(n)
138
+ row[i] = 1
139
+ constraints.append(row)
140
+ b_vector.append(1 - centers[i, 1])
141
+
142
+ # Pair constraints: r_i + r_j <= d_ij
143
+ # These prevent circles from overlapping.
144
+ for i in range(n):
145
+ for j in range(i + 1, n):
146
+ dist = np.linalg.norm(centers[i] - centers[j])
147
+ row = np.zeros(n)
148
+ row[i] = 1
149
+ row[j] = 1
150
+ constraints.append(row)
151
+ b_vector.append(dist)
152
+
153
+ A_ub = np.array(constraints)
154
+ b_ub = np.array(b_vector)
155
+
156
+ # All radii must be non-negative.
157
+ # The (0, None) bounds indicate r_i >= 0.
158
+ bounds = [(0, None) for _ in range(n)]
159
+
160
+ # Solve the linear program using the 'highs' solver for performance.
161
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
162
+
163
+ if res.success:
164
+ # Return the optimal radii found by the solver.
165
+ return res.x
166
+ else:
167
+ # Fallback in case of solver failure.
168
+ # Returning zeros implies no valid radii could be determined.
169
+ print(f"LP solver failed: {res.message}")
170
+ return np.zeros(n)
171
+ # EVOLVE-BLOCK-END
172
+
173
+
174
+ # This part remains fixed (not evolved)
175
+ def run_packing():
176
+ """Run the circle packing constructor for n=26"""
177
+ centers, radii = construct_packing()
178
+ # Calculate the sum of radii
179
+ sum_radii = np.sum(radii)
180
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/original.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EVOLVE-BLOCK-START
2
+ """
3
+ Constructor-based circle packing for n=26 circles using a modified 5x5 grid
4
+ and linear programming for optimal radii. This version introduces a diagonal
5
+ split for the central circles as a crossover from previous designs.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.optimize import linprog
10
+ import math # Added for math.radians, math.cos, math.sin
11
+
12
+
13
+ def construct_packing():
14
+ """
15
+ Constructs a packing of 26 circles based on a 5x5 grid, with the two central
16
+ circles placed diagonally to break symmetry and improve packing efficiency.
17
+ The radii are then optimized using a linear programming solver to maximize their sum.
18
+
19
+ This structure is a crossover between two approaches:
20
+ 1. It uses the high-performing 5x5 grid (24 circles) from the inspiration script.
21
+ 2. It modifies the central placement by rotating the two inner circles by 45 degrees.
22
+ This is a reasoned perturbation aimed at improving upon the previous best score.
23
+
24
+ Returns:
25
+ Tuple of (centers, radii)
26
+ centers: np.array of shape (26, 2) with (x, y) coordinates.
27
+ radii: np.array of shape (26) with radius of each circle.
28
+ """
29
+ n = 26
30
+ centers = np.zeros((n, 2))
31
+ k = 0
32
+
33
+ # 1. Place 24 circles in a 5x5 grid, skipping the central point.
34
+ # This regular grid provides a strong, stable base structure.
35
+ num_grid_divs = 5
36
+ coords = np.linspace(0.1, 0.9, num_grid_divs)
37
+
38
+ for i in range(num_grid_divs):
39
+ for j in range(num_grid_divs):
40
+ # Skip the center of the 5x5 grid (index 2, 2) to make space.
41
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
42
+ continue
43
+ centers[k] = [coords[i], coords[j]]
44
+ k += 1
45
+
46
+ # 2. Place 2 circles in the central gap with a perfectly symmetric diagonal split.
47
+ # Analysis of previous runs shows that balancing the constraints on the central circles is key.
48
+ # The two main constraints are the distance to the other central circle (d_cc) and
49
+ # the distance to the nearest grid neighbors (d_cg).
50
+ # An analytical model predicts that these two distances are equalized when the
51
+ # separation is ~0.1552 and the angle is 45 degrees. This configuration
52
+ # should remove local bottlenecks and allow the LP solver to find a more
53
+ # globally optimal solution, leading to a higher sum of radii.
54
+ central_separation_distance = 0.1552
55
+ central_asymmetry_angle_deg = 45.0 # Use perfect 45-degree symmetry for the balanced configuration
56
+
57
+ # R_prime is half the distance between the two central centers.
58
+ R_prime = central_separation_distance / 2.0
59
+ angle_rad = math.radians(central_asymmetry_angle_deg)
60
+
61
+ # Calculate offsets (dx, dy) from the square's center (0.5, 0.5).
62
+ dx = R_prime * math.cos(angle_rad)
63
+ dy = R_prime * math.sin(angle_rad)
64
+
65
+ center_point = 0.5
66
+ centers[k] = [center_point - dx, center_point - dy]
67
+ k += 1
68
+ centers[k] = [center_point + dx, center_point + dy]
69
+ k += 1
70
+
71
+ # Clip centers to be strictly within (0,1) to avoid numerical issues
72
+ # at boundaries when calculating radii. A smaller epsilon (1e-8) allows centers
73
+ # to be placed even closer to the boundaries for marginal gains, especially
74
+ # for grid circles intended to touch the walls (e.g., at 0.1 or 0.9).
75
+ centers = np.clip(centers, 1e-8, 1 - 1e-8)
76
+
77
+ # For the given centers, compute the radii that maximize the sum using LP.
78
+ radii = compute_max_radii(centers)
79
+
80
+ return centers, radii
81
+
82
+
83
+ def compute_max_radii(centers):
84
+ """
85
+ Computes the maximum possible radii for a given set of circle centers
86
+ by solving a linear programming problem. This maximizes the sum of radii
87
+ subject to non-overlapping and boundary constraints.
88
+ This function is retained from the best performing solution due to its
89
+ mathematical optimality for fixed centers.
90
+
91
+ Args:
92
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
93
+
94
+ Returns:
95
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
96
+ """
97
+ n = centers.shape[0]
98
+
99
+ # The objective is to maximize sum(radii), which is equivalent to
100
+ # minimizing sum(-radii).
101
+ c = -np.ones(n)
102
+
103
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
104
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
105
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
106
+ constraints = []
107
+ b_vector = []
108
+
109
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
110
+ # These ensure circles stay within the unit square.
111
+ for i in range(n):
112
+ # r_i <= x_i
113
+ row = np.zeros(n)
114
+ row[i] = 1
115
+ constraints.append(row)
116
+ b_vector.append(centers[i, 0])
117
+
118
+ # r_i <= 1 - x_i
119
+ row = np.zeros(n)
120
+ row[i] = 1
121
+ constraints.append(row)
122
+ b_vector.append(1 - centers[i, 0])
123
+
124
+ # r_i <= y_i
125
+ row = np.zeros(n)
126
+ row[i] = 1
127
+ constraints.append(row)
128
+ b_vector.append(centers[i, 1])
129
+
130
+ # r_i <= 1 - y_i
131
+ row = np.zeros(n)
132
+ row[i] = 1
133
+ constraints.append(row)
134
+ b_vector.append(1 - centers[i, 1])
135
+
136
+ # Pair constraints: r_i + r_j <= d_ij
137
+ # These prevent circles from overlapping.
138
+ for i in range(n):
139
+ for j in range(i + 1, n):
140
+ dist = np.linalg.norm(centers[i] - centers[j])
141
+ row = np.zeros(n)
142
+ row[i] = 1
143
+ row[j] = 1
144
+ constraints.append(row)
145
+ b_vector.append(dist)
146
+
147
+ A_ub = np.array(constraints)
148
+ b_ub = np.array(b_vector)
149
+
150
+ # All radii must be non-negative.
151
+ # The (0, None) bounds indicate r_i >= 0.
152
+ bounds = [(0, None) for _ in range(n)]
153
+
154
+ # Solve the linear program using the 'highs' solver for performance.
155
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
156
+
157
+ if res.success:
158
+ # Return the optimal radii found by the solver.
159
+ return res.x
160
+ else:
161
+ # Fallback in case of solver failure.
162
+ # Returning zeros implies no valid radii could be determined.
163
+ print(f"LP solver failed: {res.message}")
164
+ return np.zeros(n)
165
+ # EVOLVE-BLOCK-END
166
+
167
+
168
+ # This part remains fixed (not evolved)
169
+ def run_packing():
170
+ """Run the circle packing constructor for n=26"""
171
+ centers, radii = construct_packing()
172
+ # Calculate the sum of radii
173
+ sum_radii = np.sum(radii)
174
+ return centers, radii, sum_radii
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/results/correct.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "correct": true,
3
+ "error": null
4
+ }
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/results/metrics.json ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "combined_score": 2.405,
3
+ "correct": true,
4
+ "primary": {
5
+ "combined_score": 2.405,
6
+ "public": {
7
+ "centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.2900)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7100)\n centers[4] = (0.1000, 0.9000)\n centers[5] = (0.2900, 0.1000)\n centers[6] = (0.2900, 0.2900)\n centers[7] = (0.2900, 0.5000)\n centers[8] = (0.2900, 0.7100)\n centers[9] = (0.2900, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.2900)\n centers[12] = (0.5000, 0.7100)\n centers[13] = (0.5000, 0.9000)\n centers[14] = (0.7100, 0.1000)\n centers[15] = (0.7100, 0.2900)\n centers[16] = (0.7100, 0.5000)\n centers[17] = (0.7100, 0.7100)\n centers[18] = (0.7100, 0.9000)\n centers[19] = (0.9000, 0.1000)\n centers[20] = (0.9000, 0.2900)\n centers[21] = (0.9000, 0.5000)\n centers[22] = (0.9000, 0.7100)\n centers[23] = (0.9000, 0.9000)\n centers[24] = (0.4539, 0.4562)\n centers[25] = (0.5431, 0.5438)",
8
+ "num_circles": 26
9
+ },
10
+ "private": {
11
+ "reported_sum_of_radii": 2.405
12
+ },
13
+ "execution_time_mean": 0.05832532048225403,
14
+ "execution_time_std": 0.0,
15
+ "num_valid_runs": 1,
16
+ "num_invalid_runs": 0,
17
+ "all_validation_errors": [],
18
+ "correct": true,
19
+ "validation_error": null
20
+ },
21
+ "auxiliary": {
22
+ "max_circle_overlap_magnitude": 0.0,
23
+ "num_overlapping_pairs": 0,
24
+ "max_boundary_violation_magnitude": 0.0,
25
+ "num_boundary_violations": 0,
26
+ "total_area_covered": 0.713941707900842,
27
+ "packing_density": 0.713941707900842,
28
+ "empty_space_ratio": 0.286058292099158,
29
+ "avg_gap_between_circles": 0.34007118009566395,
30
+ "min_gap_between_circles": 0.0,
31
+ "num_circles": 26,
32
+ "avg_radius": 0.0925,
33
+ "std_dev_radius": 0.013576285579837874,
34
+ "min_radius": 0.05246265181582166,
35
+ "max_radius": 0.12,
36
+ "radii_coefficient_of_variation": 0.1467706549171662,
37
+ "num_unique_radii": 12,
38
+ "avg_distance_from_unit_center": 0.3703805008216262,
39
+ "avg_distance_from_unit_center_normalized": 0.523797127500483,
40
+ "max_distance_from_unit_center": 0.5656854249492381,
41
+ "center_x_std_dev": 0.2804514614049722,
42
+ "center_y_std_dev": 0.28044182699511816,
43
+ "avg_nearest_neighbor_distance_centers": 0.18216911491099536,
44
+ "center_quadrant_density_variance": 2.25,
45
+ "avg_min_distance_to_boundary": 0.09327365453496626,
46
+ "min_overall_distance_to_boundary": 0.0,
47
+ "avg_pairwise_center_distance": 0.525071180095664,
48
+ "normalized_score_per_circle": 0.0925,
49
+ "packing_aspect_ratio_of_centers_bbox": 1.0,
50
+ "avg_num_touching_neighbors": 2.076923076923077,
51
+ "avg_quadrant_radii_std_dev": 0.013482330513421369,
52
+ "primary_combined_score": 2.405
53
+ },
54
+ "auxiliary_descriptions": {
55
+ "total_area_covered": "By tracking this, we can see if increases in `sum(radii)` are also leading to proportional increases in actual area covered. If `sum(radii)` plateaus but `total_area_covered` continues to rise (or maintains a high level), it suggests a more efficient use of space by re-distributing radii or centers to fill gaps more effectively.",
56
+ "packing_density": "This is identical to `total_area_covered` because the unit square has an area of 1. It directly represents the percentage of the available area covered by circles. Higher is better, indicating more efficient use of space.",
57
+ "avg_min_distance_to_boundary": "The average of the minimum distances from the edge of each circle to any of the four boundaries of the unit square. Smaller values (closer to 0) indicate that circles are packed more tightly against the boundaries, which is often a characteristic of optimal packing solutions. Negative values would imply a violation (circle outside boundary) which the primary metric already checks. Useful for understanding how well the solution leverages the container edges.",
58
+ "min_overall_distance_to_boundary": "The single smallest distance from any circle's edge to any boundary. This metric highlights if at least one circle is positioned extremely close to a boundary, indicating a strategy to maximize space.",
59
+ "avg_radius": "The average radius of the 26 circles. This provides a central tendency for circle sizes, helping to understand if the solution favors larger, smaller, or uniformly sized circles.",
60
+ "std_dev_radius": "This metric is critical for identifying potential local optima. If `std_dev_radius` is very low and `sum(radii)` is stagnating, it might suggest the algorithm is stuck with uniform-sized circles. Encouraging greater `std_dev_radius` could lead to exploring more diverse solutions (e.g., using a few large circles and many small ones) that might break current plateaus. Conversely, if `std_dev_radius` is high, it shows exploration of diverse sizes is still active.",
61
+ "min_radius": "The smallest radius among the 26 circles. Useful for identifying if very small circles are being used to fill tiny gaps.",
62
+ "max_radius": "The largest radius among the 26 circles. Indicates the largest circle size successfully placed, which is often a key factor in maximizing total radius sum.",
63
+ "avg_nearest_neighbor_distance_centers": "This metric calculates the average Euclidean distance from each circle's center to the center of its closest neighboring circle. A lower value indicates a more tightly packed or clustered arrangement of circle centers, while a higher value suggests a more dispersed or sparse distribution. This is useful for identifying if the packing strategy is creating dense clusters or leaving significant gaps between groups of circles.",
64
+ "center_quadrant_density_variance": "This metric divides the unit square into four equal quadrants (2x2 grid) and computes the variance of the number of circle centers found within each quadrant. A lower variance indicates a more uniform distribution of circle centers across the entire packing area, suggesting a balanced utilization of space. A higher variance implies that circle centers are concentrated in certain quadrants, potentially indicating inefficient or uneven packing.",
65
+ "empty_space_ratio": "A decreasing trend in this metric, even with a stable `sum(radii)`, indicates the solution is becoming more efficient in terms of area utilization. If it's stagnant, it suggests wasted space.",
66
+ "avg_gap_between_circles": "The average positive distance between the edges of any two non-overlapping circles. A smaller average gap suggests a tighter packing configuration where circles are placed closer to each other without violating overlap constraints.",
67
+ "min_gap_between_circles": "The smallest positive distance between the edges of any two non-overlapping circles. A value near zero indicates that at least one pair of circles is almost touching, which is characteristic of optimal packing where circles are tightly constrained.",
68
+ "radii_coefficient_of_variation": "Changes in this metric can reveal if the solution is exploring new combinations of circle sizes. A sudden increase might suggest an attempt to break free from a local optimum by introducing more diverse sizes, while a decrease might indicate convergence towards a more uniform or specific size distribution.",
69
+ "avg_distance_from_unit_center": "The average Euclidean distance of all circle centers from the center of the unit square (0.5, 0.5). A lower value suggests circles are clustered more towards the center of the packing area, while a higher value indicates a more peripheral or spread-out distribution.",
70
+ "max_distance_from_unit_center": "The maximum Euclidean distance of any circle center from the center of the unit square (0.5, 0.5). This indicates how far out the outermost circle is placed, providing insight into the spread of the packing.",
71
+ "center_x_std_dev": "The standard deviation of the x-coordinates of all circle centers. A lower value indicates less spread horizontally.",
72
+ "center_y_std_dev": "The standard deviation of the y-coordinates of all circle centers. A lower value indicates less spread vertically. Together with `center_x_std_dev`, these metrics describe the spatial distribution and clustering of circles.",
73
+ "packing_aspect_ratio_of_centers_bbox": "Monitoring this metric can reveal if the packing is becoming more balanced (closer to 1.0) or if it's developing a preferred elongation. A deviation from 1.0 might suggest that the algorithm is struggling to utilize the square boundaries effectively, or it could be an emergent property of a specific packing strategy. If the primary score plateaus, but this metric moves closer to 1.0, it could indicate structural refinement.",
74
+ "avg_num_touching_neighbors": "A sustained increase in this metric, especially if the primary score is stagnant, would be a strong indicator of improved packing density and efficiency. It suggests that circles are being fit together more closely. If this metric is low and stagnating, it implies that there are significant gaps between circles that could be exploited for larger radii.",
75
+ "avg_quadrant_radii_std_dev": "This metric provides spatial insight into radii diversity. If this value is high, it means different regions of the packing are employing varied circle sizes. If it's low, it indicates a more uniform sizing strategy across the board. This can inform whether the evolution should be encouraged to explore more diverse sizes regionally or to converge on a more uniform size distribution if that proves more efficient for the overall sum of radii.",
76
+ "avg_distance_from_unit_center_normalized": "The average distance of all circle centers from the center of the unit square (0.5, 0.5), normalized by the maximum possible distance from the center (`sqrt(0.5)`). A value closer to 0 indicates a more central packing, while a value closer to 1 implies circles are pushed towards the corners or edges, utilizing the full extent of the square. Useful for assessing if the packing strategy prioritizes central arrangements or boundary-conforming ones.",
77
+ "avg_pairwise_center_distance": "The average Euclidean distance between all unique pairs of circle centers. A higher value suggests a more spread-out arrangement of circles, potentially indicating a less \"clustered\" or more uniform distribution of centers. This complements nearest-neighbor distance by considering all pairs and provides a global measure of center dispersion.",
78
+ "avg_min_boundary_distance": "As solutions converge, an efficient packing should optimally utilize the boundaries. A consistently low (approaching zero) `avg_min_boundary_distance` combined with a high primary score indicates a well-optimized solution that is effectively using the edges of the unit square. If this value is still relatively high, it might suggest opportunities to push circles closer to the container's perimeter, thereby creating more space internally for larger circles or more circles."
79
+ },
80
+ "timestamp": 1770497811.3328636,
81
+ "generation": 104
82
+ }
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_104/search_replace.txt ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ I will propose two independent changes. The first reverts the central circle parameters to the empirically superior configuration from past runs to recover the best-known score of 2.52. The second introduces a novel modification to the grid layout, designed to create more space in the crowded central area, which is a promising strategy for pushing beyond the current local optimum.
2
+
3
+ <NAME>
4
+ non_uniform_grid_spacing
5
+ </NAME>
6
+ <DESCRIPTION>
7
+ The current configuration uses a uniform 5x5 grid defined by `np.linspace(0.1, 0.9, 5)`, resulting in coordinates `[0.1, 0.3, 0.5, 0.7, 0.9]`. Geometric analysis shows that the central area is the most constrained part of the packing, limiting the radii of the two central circles and their eight neighbors.
8
+
9
+ This edit introduces a non-uniform grid to strategically create more space in the center. I modify the grid coordinates by pushing the inner grid lines (originally at 0.3 and 0.7) slightly outwards by `0.01` to `0.29` and `0.71` respectively. This expands the central cell from `0.4x0.4` to `0.42x0.42`. This targeted relaxation of central constraints is hypothesized to allow the two central circles and their neighbors to grow, leading to a larger overall sum of radii, even if it slightly compresses the space in the outer bands. This is a common strategy in packing problems: creating space where it is most valuable to unlock a better global solution.
10
+ </DESCRIPTION>
11
+ <DIFF>
12
+ <<<<<<< SEARCH
13
+ # 1. Place 24 circles in a 5x5 grid, skipping the central point.
14
+ # This regular grid provides a strong, stable base structure.
15
+ num_grid_divs = 5
16
+ coords = np.linspace(0.1, 0.9, num_grid_divs)
17
+ =======
18
+ # 1. Place 24 circles in a non-uniform 5x5 grid, skipping the central point.
19
+ # The grid is modified to give more space to the central region. By pushing the
20
+ # inner grid lines (originally at 0.3 and 0.7) slightly outwards to 0.29 and 0.71,
21
+ # we increase the size of the central cell. This should relax constraints on the
22
+ # two central circles, allowing for larger radii.
23
+ num_grid_divs = 5
24
+ inner_grid_offset = 0.01
25
+ coords = np.array([0.1, 0.3 - inner_grid_offset, 0.5, 0.7 + inner_grid_offset, 0.9])
26
+ >>>>>>> REPLACE
27
+ </DIFF>
28
+ <NAME>
29
+ revert_to_best_central_params
30
+ </NAME>
31
+ <DESCRIPTION>
32
+ The current program uses a `central_separation_distance` of `0.1552` and an `angle` of `45.0` degrees, based on an analytical model of balancing constraints. However, empirical results from previous runs consistently show that this configuration (score: 2.51) is outperformed by one with a smaller separation, slight asymmetry, and a small offset (`separation=0.125`, `angle=44.5`, `offset_x=-0.0015`), which achieved a score of 2.52.
33
+
34
+ This edit reverts the parameters for the two central circles to this empirically superior configuration. While analytical models are valuable, slight asymmetry often prevents "geometric locking" and allows the optimization solver to find a better overall arrangement. This change reinstates the best-known parameters to recover performance and provide a stronger baseline for future experiments, such as the non-uniform grid modification.
35
+ </DESCRIPTION>
36
+ <DIFF>
37
+ <<<<<<< SEARCH
38
+ # 2. Place 2 circles in the central gap with a perfectly symmetric diagonal split.
39
+ # Analysis of previous runs shows that balancing the constraints on the central circles is key.
40
+ # The two main constraints are the distance to the other central circle (d_cc) and
41
+ # the distance to the nearest grid neighbors (d_cg).
42
+ # An analytical model predicts that these two distances are equalized when the
43
+ # separation is ~0.1552 and the angle is 45 degrees. This configuration
44
+ # should remove local bottlenecks and allow the LP solver to find a more
45
+ # globally optimal solution, leading to a higher sum of radii.
46
+ central_separation_distance = 0.1552
47
+ central_asymmetry_angle_deg = 45.0 # Use perfect 45-degree symmetry for the balanced configuration
48
+
49
+ # R_prime is half the distance between the two central centers.
50
+ R_prime = central_separation_distance / 2.0
51
+ angle_rad = math.radians(central_asymmetry_angle_deg)
52
+
53
+ # Calculate offsets (dx, dy) from the square's center (0.5, 0.5).
54
+ dx = R_prime * math.cos(angle_rad)
55
+ dy = R_prime * math.sin(angle_rad)
56
+
57
+ center_point = 0.5
58
+ centers[k] = [center_point - dx, center_point - dy]
59
+ k += 1
60
+ centers[k] = [center_point + dx, center_point + dy]
61
+ =======
62
+ # 2. Place 2 circles in the central gap, using empirically superior parameters.
63
+ # Previous runs have consistently shown that a separation of 0.125 and a slight
64
+ # asymmetry angle of 44.5 degrees yield a higher score (2.52) than the analytically
65
+ # "balanced" but symmetric configuration (2.51). This change reinstates these
66
+ # high-performing values and re-introduces a small horizontal offset to the pair's
67
+ # centroid, which was part of the best-known setup and breaks further symmetry.
68
+ central_separation_distance = 0.125
69
+ central_asymmetry_angle_deg = 44.5
70
+ central_pair_offset_x = -0.0015
71
+
72
+ # R_prime is half the distance between the two central centers.
73
+ R_prime = central_separation_distance / 2.0
74
+ angle_rad = math.radians(central_asymmetry_angle_deg)
75
+
76
+ # Calculate internal offsets (dx, dy) relative to the pair's center.
77
+ dx = R_prime * math.cos(angle_rad)
78
+ dy = R_prime * math.sin(angle_rad)
79
+
80
+ # The center of the pair is shifted slightly off (0.5, 0.5).
81
+ center_pair_x = 0.5 + central_pair_offset_x
82
+ center_pair_y = 0.5
83
+
84
+ centers[k] = [center_pair_x - dx, center_pair_y - dy]
85
+ k += 1
86
+ centers[k] = [center_pair_x + dx, center_pair_y + dy]
87
+ >>>>>>> REPLACE
88
+ </DIFF>
examples_deprecated/circle_packing/results/results_full_gen200_period20_20260207_182944/gen_106/edit.diff ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/original.py
2
+ +++ b/original.py
3
+ @@ -1,205 +1,199 @@
4
+ # EVOLVE-BLOCK-START
5
+ import numpy as np
6
+ from scipy.optimize import linprog
7
+ from dataclasses import dataclass
8
+ import math
9
+
10
+
11
+ @dataclass
12
+ class CirclePackingConfig:
13
+ """
14
+ Configuration parameters for the circle packing problem.
15
+ Encapsulates all tunable parameters for clarity and easy modification.
16
+ """
17
+ n_circles: int = 26
18
+ grid_num_divs: int = 5
19
+ - grid_margin: float = 0.1
20
+ + # Increase margin to allow perimeter circles to grow, at the cost of shrinking the inner grid.
21
+ + grid_margin: float = 0.11
22
+ # Euclidean distance between the centers of the two central circles.
23
+ - # This value (0.107) is adopted from a previous high-scoring solution (2.51).
24
+ - central_separation_distance: float = 0.107
25
+ - # Offset of the first central circle from the (0.5, 0.5) center point.
26
+ - # Initial values derived from the previously best-performing central_asymmetry_angle_deg = 44.5
27
+ - central_circle1_x_offset: float = -0.038195
28
+ - central_circle1_y_offset: float = -0.037434
29
+ - # Angle in degrees defining the orientation from the first central circle to the second.
30
+ - # Initial value matches the previous central_asymmetry_angle_deg = 44.5
31
+ - central_pair_orientation_angle_deg: float = 44.5
32
+ + # This is reduced to compensate for the smaller central hole caused by the increased grid_margin.
33
+ + central_separation_distance: float = 0.104
34
+ + # Angle in degrees for the asymmetric diagonal placement of central circles, centered at (0.5, 0.5).
35
+ + # A value of 44.5 degrees breaks perfect symmetry and is retained from prior high-scoring runs.
36
+ + central_asymmetry_angle_deg: float = 44.5
37
+ clip_epsilon: float = 1e-8
38
+
39
+
40
+ def _place_grid_circles(config: CirclePackingConfig) -> np.ndarray:
41
+ """
42
+ Generates centers for 24 circles arranged in a 5x5 grid, skipping the center.
43
+ The grid with a 0.1 margin is a proven, high-performing base structure.
44
+ """
45
+ num_grid_divs = config.grid_num_divs
46
+ m = config.grid_margin
47
+ coords = np.linspace(m, 1.0 - m, num_grid_divs)
48
+
49
+ grid_centers = []
50
+ for i in range(num_grid_divs):
51
+ for j in range(num_grid_divs):
52
+ # Skip the center of the 5x5 grid (index 2, 2)
53
+ if i == num_grid_divs // 2 and j == num_grid_divs // 2:
54
+ continue
55
+ grid_centers.append([coords[i], coords[j]])
56
+ return np.array(grid_centers)
57
+
58
+
59
+ def _place_central_circles(config: CirclePackingConfig) -> np.ndarray:
60
+ """
61
+ - Generates centers for 2 circles placed with a parameterized asymmetric diagonal split.
62
+ - This method allows for independent positioning of the first central circle and
63
+ - then defines the second central circle relative to the first, providing maximum flexibility.
64
+ + Generates centers for 2 circles placed with a parameterized asymmetric diagonal split,
65
+ + symmetrically around the square's center (0.5, 0.5). This provides a more intuitive
66
+ + and stable parameterization than defining one circle relative to the other.
67
+ """
68
+ central_separation_distance = config.central_separation_distance
69
+ - central_circle1_x_offset = config.central_circle1_x_offset
70
+ - central_circle1_y_offset = config.central_circle1_y_offset
71
+ - central_pair_orientation_angle_deg = config.central_pair_orientation_angle_deg
72
+ + central_asymmetry_angle_deg = config.central_asymmetry_angle_deg
73
+ +
74
+ + # R_prime is the distance from the center (0.5, 0.5) to each central circle's center.
75
+ + R_prime = central_separation_distance / 2.0
76
+ +
77
+ + # Convert the asymmetry angle from degrees to radians for trigonometric functions.
78
+ + angle_rad = math.radians(central_asymmetry_angle_deg)
79
+ +
80
+ + # Calculate x and y offsets (dx, dy) from the center (0.5, 0.5).
81
+ + dx = R_prime * math.cos(angle_rad)
82
+ + dy = R_prime * math.sin(angle_rad)
83
+
84
+ center_point = 0.5
85
+ -
86
+ - # Position the first central circle based on its offset from the unit square's center
87
+ - c1_x = center_point + central_circle1_x_offset
88
+ - c1_y = center_point + central_circle1_y_offset
89
+ -
90
+ - # Calculate the position of the second central circle relative to the first
91
+ - # based on the separation distance and orientation angle.
92
+ - angle_rad = math.radians(central_pair_orientation_angle_deg)
93
+ - c2_x = c1_x + central_separation_distance * math.cos(angle_rad)
94
+ - c2_y = c1_y + central_separation_distance * math.sin(angle_rad)
95
+ -
96
+ central_centers = np.array([
97
+ - [c1_x, c1_y],
98
+ - [c2_x, c2_y]
99
+ + [center_point - dx, center_point - dy],
100
+ + [center_point + dx, center_point + dy]
101
+ ])
102
+ return central_centers
103
+
104
+
105
+ def generate_centers(config: CirclePackingConfig) -> np.ndarray:
106
+ """
107
+ Combines different placement strategies to generate all 26 circle centers.
108
+ """
109
+ grid_centers = _place_grid_circles(config)
110
+ central_centers = _place_central_circles(config)
111
+
112
+ # Combine all centers into a single array.
113
+ all_centers = np.vstack((grid_centers, central_centers))
114
+
115
+ # Clip centers to be strictly within the unit square.
116
+ all_centers = np.clip(all_centers, config.clip_epsilon, 1 - config.clip_epsilon)
117
+
118
+ return all_centers
119
+
120
+
121
+ def construct_packing():
122
+ """
123
+ Constructs a packing of 26 circles by first generating centers using a
124
+ hierarchical approach and then optimizing their radii via linear programming.
125
+ This version refactors the code into a modular, configuration-driven design and
126
+ adopts the best-performing parameters from prior experiments.
127
+ """
128
+ # Initialize configuration with empirically optimized parameters.
129
+ config = CirclePackingConfig()
130
+
131
+ # Generate all circle centers based on the configuration.
132
+ centers = generate_centers(config)
133
+
134
+ # Compute the optimal radii for these fixed centers.
135
+ radii = compute_max_radii(centers)
136
+
137
+ return centers, radii
138
+
139
+
140
+ def compute_max_radii(centers):
141
+ """
142
+ Computes the maximum possible radii for a given set of circle centers
143
+ by solving a linear programming problem. This maximizes the sum of radii
144
+ subject to non-overlapping and boundary constraints.
145
+ This function is retained from the best performing solution due to its
146
+ mathematical optimality for fixed centers.
147
+
148
+ Args:
149
+ centers: np.array of shape (n, 2) with (x, y) coordinates.
150
+
151
+ Returns:
152
+ np.ndarray: An array of shape (n) with the optimal radius for each circle.
153
+ """
154
+ n = centers.shape[0]
155
+
156
+ # The objective is to maximize sum(radii), which is equivalent to
157
+ # minimizing sum(-radii).
158
+ c = -np.ones(n)
159
+
160
+ # Inequality constraints (A_ub * r <= b_ub) are built as a list of rows.
161
+ # 1. r_i + r_j <= dist(c_i, c_j) for each pair of circles (i, j)
162
+ # 2. r_i <= dist_to_wall for each circle i (4 constraints per circle)
163
+ constraints = []
164
+ b_vector = []
165
+
166
+ # Wall constraints: r_i <= x_i, r_i <= 1-x_i, r_i <= y_i, r_i <= 1-y_i
167
+ # These ensure circles stay within the unit square.
168
+ for i in range(n):
169
+ # r_i <= x_i
170
+ row = np.zeros(n)
171
+ row[i] = 1
172
+ constraints.append(row)
173
+ b_vector.append(centers[i, 0])
174
+
175
+ # r_i <= 1 - x_i
176
+ row = np.zeros(n)
177
+ row[i] = 1
178
+ constraints.append(row)
179
+ b_vector.append(1 - centers[i, 0])
180
+
181
+ # r_i <= y_i
182
+ row = np.zeros(n)
183
+ row[i] = 1
184
+ constraints.append(row)
185
+ b_vector.append(centers[i, 1])
186
+
187
+ # r_i <= 1 - y_i
188
+ row = np.zeros(n)
189
+ row[i] = 1
190
+ constraints.append(row)
191
+ b_vector.append(1 - centers[i, 1])
192
+
193
+ # Pair constraints: r_i + r_j <= d_ij
194
+ # These prevent circles from overlapping.
195
+ for i in range(n):
196
+ for j in range(i + 1, n):
197
+ dist = np.linalg.norm(centers[i] - centers[j])
198
+ row = np.zeros(n)
199
+ row[i] = 1
200
+ row[j] = 1
201
+ constraints.append(row)
202
+ b_vector.append(dist)
203
+
204
+ A_ub = np.array(constraints)
205
+ b_ub = np.array(b_vector)
206
+
207
+ # All radii must be non-negative.
208
+ # The (0, None) bounds indicate r_i >= 0.
209
+ bounds = [(0, None) for _ in range(n)]
210
+
211
+ # Solve the linear program using the 'highs' solver for performance.
212
+ res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
213
+
214
+ if res.success:
215
+ # Return the optimal radii found by the solver.
216
+ return res.x
217
+ else:
218
+ # Fallback in case of solver failure.
219
+ # Returning zeros implies no valid radii could be determined.
220
+ print(f"LP solver failed: {res.message}")
221
+ return np.zeros(n)
222
+ # EVOLVE-BLOCK-END
223
+
224
+
225
+ # This part remains fixed (not evolved)
226
+ def run_packing():
227
+ """Run the circle packing constructor for n=26"""
228
+ centers, radii = construct_packing()
229
+ # Calculate the sum of radii
230
+ sum_radii = np.sum(radii)
231
+ return centers, radii, sum_radii