Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/edit.diff +171 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/main.py +134 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/original.py +145 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/results/metrics.json +89 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/rewrite.txt +125 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/eval_agent_memory/__pycache__/auxiliary_metrics.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results/job_log.err +9 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results/job_log.out +16 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results/metrics.json +15 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_1/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_1/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_1/results/metrics.json +25 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/edit.diff +228 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/main.py +208 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/original.py +190 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/results/metrics.json +41 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/search_replace.txt +91 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/edit.diff +232 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/main.py +186 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/original.py +190 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/results/metrics.json +41 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/rewrite.txt +177 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_12/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_12/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_12/results/metrics.json +41 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_13/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_13/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_13/results/metrics.json +43 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_14/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_14/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_14/results/metrics.json +41 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_15/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_15/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_15/results/metrics.json +46 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_16/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_16/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_16/results/metrics.json +46 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_17/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_17/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_17/results/metrics.json +46 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_18/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_18/edit.diff +259 -0
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (3.71 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/edit.diff
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--- a/original.py
|
| 2 |
+
+++ b/original.py
|
| 3 |
+
@@ -1,145 +1,134 @@
|
| 4 |
+
# EVOLVE-BLOCK-START
|
| 5 |
+
"""Constructor-based circle packing for n=26 circles"""
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def construct_packing():
|
| 11 |
+
"""
|
| 12 |
+
Construct a specific arrangement of 26 circles in a unit square
|
| 13 |
+
that attempts to maximize the sum of their radii.
|
| 14 |
+
|
| 15 |
+
Returns:
|
| 16 |
+
- Tuple of (centers, radii, sum_of_radii)
|
| 17 |
+
+ 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 |
+
- sum_of_radii: Sum of all radii
|
| 21 |
+
"""
|
| 22 |
+
# Initialize arrays for 26 circles
|
| 23 |
+
n = 26
|
| 24 |
+
centers = np.zeros((n, 2))
|
| 25 |
+
|
| 26 |
+
# Strategy: Place circles in an uneven grid pattern (3 rows of 6, 2 rows of 4)
|
| 27 |
+
- # This accounts for all 26 circles and aims for better space utilization than
|
| 28 |
+
- # the previous concentric ring pattern with clipping.
|
| 29 |
+
+ # This accounts for all 26 circles and aims for better space utilization.
|
| 30 |
+
|
| 31 |
+
# Y-coordinates for 5 logical rows
|
| 32 |
+
- # The centers will be at 0.1, 0.3, 0.5, 0.7, 0.9. This implies a max radius of 0.1 for y-dimension.
|
| 33 |
+
- # Optimized Y-coordinates for 5 logical rows, accounting for different circle sizes in alternating rows.
|
| 34 |
+
- # This calculation assumes circles touch vertically, using adjusted 'r_small' and 'r_large' values.
|
| 35 |
+
- # The total vertical height is 6*r_small + 4*r_large = 1.0 for perfect vertical filling.
|
| 36 |
+
- # We introduce a factor to slightly adjust r_small from its initial 1/12 estimate,
|
| 37 |
+
- # then derive r_large to maintain the total height constraint.
|
| 38 |
+
- r_small_base = 1.0 / 12.0
|
| 39 |
+
- # Tunable parameter: 0.99 makes r_small slightly smaller, potentially allowing r_large to increase
|
| 40 |
+
- # since there are fewer 4-circle rows than 6-circle rows.
|
| 41 |
+
- r_small_factor = 0.99
|
| 42 |
+
- r_small = r_small_base * r_small_factor
|
| 43 |
+
+ # The y-coordinates are calculated based on 'ideal' radii for 6-circle rows (r_small)
|
| 44 |
+
+ # and 4-circle rows (r_large), assuming a vertical tangency model as a baseline.
|
| 45 |
+
+ # The constraint 6*r_small + 4*r_large = 1.0 defines how these radii fill the vertical space.
|
| 46 |
+
+ # We revert to the default r_small = 1/12 and r_large = 1/8, which previously yielded good results.
|
| 47 |
+
+ r_small = 1.0 / 12.0
|
| 48 |
+
+ r_large = 1.0 / 8.0
|
| 49 |
+
|
| 50 |
+
- # Calculate r_large to ensure 6*r_small + 4*r_large = 1.0 (vertical filling)
|
| 51 |
+
- # This formula maintains the constraint that the entire vertical space is filled by the circles
|
| 52 |
+
- # if they were to perfectly align and touch with these radii.
|
| 53 |
+
- r_large = (1.0 - 6 * r_small) / 4.0
|
| 54 |
+
-
|
| 55 |
+
+ # Baseline y-coordinates based on ideal vertical tangency
|
| 56 |
+
y_coords = np.array([
|
| 57 |
+
r_small, # Bottom 6-circle row (y_0)
|
| 58 |
+
r_small + r_small + r_large, # First 4-circle row (y_1)
|
| 59 |
+
r_small + r_small + r_large + r_large + r_small, # Middle 6-circle row (y_2)
|
| 60 |
+
r_small + r_small + r_large + r_large + r_small + r_small + r_large, # Second 4-circle row (y_3)
|
| 61 |
+
1.0 - r_small # Top 6-circle row (y_4) - ensures symmetry and total height
|
| 62 |
+
])
|
| 63 |
+
- # For r_small_factor = 0.99: r_small approx 0.0825, r_large approx 0.12625
|
| 64 |
+
- # y_coords approx: [0.0825, 0.29125, 0.5, 0.70875, 0.9175]
|
| 65 |
+
|
| 66 |
+
+ # New tunable parameter: Micro-Vertical Shift for 4-Circle Rows (Recommendation 5)
|
| 67 |
+
+ # This slightly perturbs the y-coordinates of the 4-circle rows to explore non-ideal
|
| 68 |
+
+ # vertical tangency arrangements that might lead to larger overall radii.
|
| 69 |
+
+ y_shift_4_rows = -0.001 # Small negative shift
|
| 70 |
+
+
|
| 71 |
+
+ # Apply the shift to the two 4-circle rows
|
| 72 |
+
+ y_coords[1] += y_shift_4_rows
|
| 73 |
+
+ y_coords[3] += y_shift_4_rows
|
| 74 |
+
+
|
| 75 |
+
# X-coordinates for rows with 6 circles (3 rows)
|
| 76 |
+
- # Implies max radius of 1/(2*6) = 1/12 for x-dimension
|
| 77 |
+
- x_coords_6 = np.linspace(1.0 / (2 * 6), 1.0 - 1.0 / (2 * 6), 6) # centers at 1/12, 3/12, ..., 11/12
|
| 78 |
+
+ # Fixed based on previous successful iterations.
|
| 79 |
+
+ x_coords_6 = np.linspace(1.0 / (2 * 6), 1.0 - 1.0 / (2 * 6), 6)
|
| 80 |
+
|
| 81 |
+
# X-coordinates for rows with 4 circles (2 rows)
|
| 82 |
+
- # This reverts to a non-staggered grid which empirically performed better in previous runs.
|
| 83 |
+
- # The centers are at 1/8, 3/8, 5/8, 7/8.
|
| 84 |
+
+ # Fixed based on previous successful iterations (non-staggered relative to the boundaries).
|
| 85 |
+
x_coords_4 = np.linspace(1.0 / (2 * 4), 1.0 - 1.0 / (2 * 4), 4)
|
| 86 |
+
|
| 87 |
+
k = 0
|
| 88 |
+
- # Rows with 6 circles: y_coords[0], y_coords[2], y_coords[4] (i.e., y=0.1, 0.5, 0.9)
|
| 89 |
+
+ # Rows with 6 circles: y_coords[0], y_coords[2], y_coords[4]
|
| 90 |
+
for i in [0, 2, 4]:
|
| 91 |
+
for x_coord in x_coords_6:
|
| 92 |
+
centers[k, 0] = x_coord
|
| 93 |
+
centers[k, 1] = y_coords[i]
|
| 94 |
+
k += 1
|
| 95 |
+
|
| 96 |
+
- # Rows with 4 circles: y_coords[1], y_coords[3] (i.e., y=0.3, 0.7)
|
| 97 |
+
+ # Rows with 4 circles: y_coords[1], y_coords[3]
|
| 98 |
+
for i in [1, 3]:
|
| 99 |
+
for x_coord in x_coords_4:
|
| 100 |
+
centers[k, 0] = x_coord
|
| 101 |
+
centers[k, 1] = y_coords[i]
|
| 102 |
+
k += 1
|
| 103 |
+
-
|
| 104 |
+
- # The previous clipping was detrimental for configurations where circles
|
| 105 |
+
- # are intended to be very close to the boundaries, e.g., for optimal grid packing.
|
| 106 |
+
- # The compute_max_radii function already handles boundary conditions.
|
| 107 |
+
|
| 108 |
+
radii = compute_max_radii(centers)
|
| 109 |
+
return centers, radii
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def compute_max_radii(centers):
|
| 113 |
+
"""
|
| 114 |
+
Compute the maximum possible radii for each circle position
|
| 115 |
+
such that they don't overlap and stay within the unit square, using
|
| 116 |
+
an iterative approach to resolve overlaps.
|
| 117 |
+
|
| 118 |
+
Args:
|
| 119 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates
|
| 120 |
+
|
| 121 |
+
Returns:
|
| 122 |
+
np.array of shape (n) with radius of each circle
|
| 123 |
+
"""
|
| 124 |
+
n = centers.shape[0]
|
| 125 |
+
radii = np.zeros(n)
|
| 126 |
+
|
| 127 |
+
# Initialize radii to the maximum possible value limited by boundaries
|
| 128 |
+
for i in range(n):
|
| 129 |
+
x, y = centers[i]
|
| 130 |
+
radii[i] = min(x, y, 1 - x, 1 - y)
|
| 131 |
+
|
| 132 |
+
# Pre-compute distances for efficiency
|
| 133 |
+
dists = np.zeros((n, n))
|
| 134 |
+
for i in range(n):
|
| 135 |
+
for j in range(i + 1, n):
|
| 136 |
+
d = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
|
| 137 |
+
dists[i, j] = dists[j, i] = d
|
| 138 |
+
|
| 139 |
+
# Iteratively resolve overlaps by proportionally scaling down overlapping pairs.
|
| 140 |
+
# This is repeated until no significant changes occur, ensuring a stable solution.
|
| 141 |
+
for _ in range(500):
|
| 142 |
+
changed_in_pass = False
|
| 143 |
+
for i in range(n):
|
| 144 |
+
for j in range(i + 1, n):
|
| 145 |
+
dist = dists[i, j]
|
| 146 |
+
|
| 147 |
+
# If current radii cause an overlap (with a small tolerance)
|
| 148 |
+
if radii[i] + radii[j] > dist + 1e-12:
|
| 149 |
+
changed_in_pass = True
|
| 150 |
+
# Calculate the scaling factor to make them just touch
|
| 151 |
+
scale = dist / (radii[i] + radii[j])
|
| 152 |
+
radii[i] *= scale
|
| 153 |
+
radii[j] *= scale
|
| 154 |
+
|
| 155 |
+
# If a full pass resulted in no changes, the packing has stabilized
|
| 156 |
+
if not changed_in_pass:
|
| 157 |
+
break
|
| 158 |
+
|
| 159 |
+
return radii
|
| 160 |
+
-
|
| 161 |
+
-
|
| 162 |
+
# EVOLVE-BLOCK-END
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
# This part remains fixed (not evolved)
|
| 166 |
+
def run_packing():
|
| 167 |
+
"""Run the circle packing constructor for n=26"""
|
| 168 |
+
centers, radii = construct_packing()
|
| 169 |
+
# Calculate the sum of radii
|
| 170 |
+
sum_radii = np.sum(radii)
|
| 171 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/main.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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)
|
| 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 |
+
"""
|
| 17 |
+
# Initialize arrays for 26 circles
|
| 18 |
+
n = 26
|
| 19 |
+
centers = np.zeros((n, 2))
|
| 20 |
+
|
| 21 |
+
# Strategy: Place circles in an uneven grid pattern (3 rows of 6, 2 rows of 4)
|
| 22 |
+
# This accounts for all 26 circles and aims for better space utilization.
|
| 23 |
+
|
| 24 |
+
# Y-coordinates for 5 logical rows
|
| 25 |
+
# The y-coordinates are calculated based on 'ideal' radii for 6-circle rows (r_small)
|
| 26 |
+
# and 4-circle rows (r_large), assuming a vertical tangency model as a baseline.
|
| 27 |
+
# The constraint 6*r_small + 4*r_large = 1.0 defines how these radii fill the vertical space.
|
| 28 |
+
# We revert to the default r_small = 1/12 and r_large = 1/8, which previously yielded good results.
|
| 29 |
+
r_small = 1.0 / 12.0
|
| 30 |
+
r_large = 1.0 / 8.0
|
| 31 |
+
|
| 32 |
+
# Baseline y-coordinates based on ideal vertical tangency
|
| 33 |
+
y_coords = np.array([
|
| 34 |
+
r_small, # Bottom 6-circle row (y_0)
|
| 35 |
+
r_small + r_small + r_large, # First 4-circle row (y_1)
|
| 36 |
+
r_small + r_small + r_large + r_large + r_small, # Middle 6-circle row (y_2)
|
| 37 |
+
r_small + r_small + r_large + r_large + r_small + r_small + r_large, # Second 4-circle row (y_3)
|
| 38 |
+
1.0 - r_small # Top 6-circle row (y_4) - ensures symmetry and total height
|
| 39 |
+
])
|
| 40 |
+
|
| 41 |
+
# New tunable parameter: Micro-Vertical Shift for 4-Circle Rows (Recommendation 5)
|
| 42 |
+
# This slightly perturbs the y-coordinates of the 4-circle rows to explore non-ideal
|
| 43 |
+
# vertical tangency arrangements that might lead to larger overall radii.
|
| 44 |
+
y_shift_4_rows = -0.001 # Small negative shift
|
| 45 |
+
|
| 46 |
+
# Apply the shift to the two 4-circle rows
|
| 47 |
+
y_coords[1] += y_shift_4_rows
|
| 48 |
+
y_coords[3] += y_shift_4_rows
|
| 49 |
+
|
| 50 |
+
# X-coordinates for rows with 6 circles (3 rows)
|
| 51 |
+
# Fixed based on previous successful iterations.
|
| 52 |
+
x_coords_6 = np.linspace(1.0 / (2 * 6), 1.0 - 1.0 / (2 * 6), 6)
|
| 53 |
+
|
| 54 |
+
# X-coordinates for rows with 4 circles (2 rows)
|
| 55 |
+
# Fixed based on previous successful iterations (non-staggered relative to the boundaries).
|
| 56 |
+
x_coords_4 = np.linspace(1.0 / (2 * 4), 1.0 - 1.0 / (2 * 4), 4)
|
| 57 |
+
|
| 58 |
+
k = 0
|
| 59 |
+
# Rows with 6 circles: y_coords[0], y_coords[2], y_coords[4]
|
| 60 |
+
for i in [0, 2, 4]:
|
| 61 |
+
for x_coord in x_coords_6:
|
| 62 |
+
centers[k, 0] = x_coord
|
| 63 |
+
centers[k, 1] = y_coords[i]
|
| 64 |
+
k += 1
|
| 65 |
+
|
| 66 |
+
# Rows with 4 circles: y_coords[1], y_coords[3]
|
| 67 |
+
for i in [1, 3]:
|
| 68 |
+
for x_coord in x_coords_4:
|
| 69 |
+
centers[k, 0] = x_coord
|
| 70 |
+
centers[k, 1] = y_coords[i]
|
| 71 |
+
k += 1
|
| 72 |
+
|
| 73 |
+
radii = compute_max_radii(centers)
|
| 74 |
+
return centers, radii
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def compute_max_radii(centers):
|
| 78 |
+
"""
|
| 79 |
+
Compute the maximum possible radii for each circle position
|
| 80 |
+
such that they don't overlap and stay within the unit square, using
|
| 81 |
+
an iterative approach to resolve overlaps.
|
| 82 |
+
|
| 83 |
+
Args:
|
| 84 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates
|
| 85 |
+
|
| 86 |
+
Returns:
|
| 87 |
+
np.array of shape (n) with radius of each circle
|
| 88 |
+
"""
|
| 89 |
+
n = centers.shape[0]
|
| 90 |
+
radii = np.zeros(n)
|
| 91 |
+
|
| 92 |
+
# Initialize radii to the maximum possible value limited by boundaries
|
| 93 |
+
for i in range(n):
|
| 94 |
+
x, y = centers[i]
|
| 95 |
+
radii[i] = min(x, y, 1 - x, 1 - y)
|
| 96 |
+
|
| 97 |
+
# Pre-compute distances for efficiency
|
| 98 |
+
dists = np.zeros((n, n))
|
| 99 |
+
for i in range(n):
|
| 100 |
+
for j in range(i + 1, n):
|
| 101 |
+
d = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
|
| 102 |
+
dists[i, j] = dists[j, i] = d
|
| 103 |
+
|
| 104 |
+
# Iteratively resolve overlaps by proportionally scaling down overlapping pairs.
|
| 105 |
+
# This is repeated until no significant changes occur, ensuring a stable solution.
|
| 106 |
+
for _ in range(500):
|
| 107 |
+
changed_in_pass = False
|
| 108 |
+
for i in range(n):
|
| 109 |
+
for j in range(i + 1, n):
|
| 110 |
+
dist = dists[i, j]
|
| 111 |
+
|
| 112 |
+
# If current radii cause an overlap (with a small tolerance)
|
| 113 |
+
if radii[i] + radii[j] > dist + 1e-12:
|
| 114 |
+
changed_in_pass = True
|
| 115 |
+
# Calculate the scaling factor to make them just touch
|
| 116 |
+
scale = dist / (radii[i] + radii[j])
|
| 117 |
+
radii[i] *= scale
|
| 118 |
+
radii[j] *= scale
|
| 119 |
+
|
| 120 |
+
# If a full pass resulted in no changes, the packing has stabilized
|
| 121 |
+
if not changed_in_pass:
|
| 122 |
+
break
|
| 123 |
+
|
| 124 |
+
return radii
|
| 125 |
+
# EVOLVE-BLOCK-END
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
# This part remains fixed (not evolved)
|
| 129 |
+
def run_packing():
|
| 130 |
+
"""Run the circle packing constructor for n=26"""
|
| 131 |
+
centers, radii = construct_packing()
|
| 132 |
+
# Calculate the sum of radii
|
| 133 |
+
sum_radii = np.sum(radii)
|
| 134 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/original.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
# Strategy: Place circles in an uneven grid pattern (3 rows of 6, 2 rows of 4)
|
| 23 |
+
# This accounts for all 26 circles and aims for better space utilization than
|
| 24 |
+
# the previous concentric ring pattern with clipping.
|
| 25 |
+
|
| 26 |
+
# Y-coordinates for 5 logical rows
|
| 27 |
+
# The centers will be at 0.1, 0.3, 0.5, 0.7, 0.9. This implies a max radius of 0.1 for y-dimension.
|
| 28 |
+
# Optimized Y-coordinates for 5 logical rows, accounting for different circle sizes in alternating rows.
|
| 29 |
+
# This calculation assumes circles touch vertically, using adjusted 'r_small' and 'r_large' values.
|
| 30 |
+
# The total vertical height is 6*r_small + 4*r_large = 1.0 for perfect vertical filling.
|
| 31 |
+
# We introduce a factor to slightly adjust r_small from its initial 1/12 estimate,
|
| 32 |
+
# then derive r_large to maintain the total height constraint.
|
| 33 |
+
r_small_base = 1.0 / 12.0
|
| 34 |
+
# Tunable parameter: 0.99 makes r_small slightly smaller, potentially allowing r_large to increase
|
| 35 |
+
# since there are fewer 4-circle rows than 6-circle rows.
|
| 36 |
+
r_small_factor = 0.99
|
| 37 |
+
r_small = r_small_base * r_small_factor
|
| 38 |
+
|
| 39 |
+
# Calculate r_large to ensure 6*r_small + 4*r_large = 1.0 (vertical filling)
|
| 40 |
+
# This formula maintains the constraint that the entire vertical space is filled by the circles
|
| 41 |
+
# if they were to perfectly align and touch with these radii.
|
| 42 |
+
r_large = (1.0 - 6 * r_small) / 4.0
|
| 43 |
+
|
| 44 |
+
y_coords = np.array([
|
| 45 |
+
r_small, # Bottom 6-circle row (y_0)
|
| 46 |
+
r_small + r_small + r_large, # First 4-circle row (y_1)
|
| 47 |
+
r_small + r_small + r_large + r_large + r_small, # Middle 6-circle row (y_2)
|
| 48 |
+
r_small + r_small + r_large + r_large + r_small + r_small + r_large, # Second 4-circle row (y_3)
|
| 49 |
+
1.0 - r_small # Top 6-circle row (y_4) - ensures symmetry and total height
|
| 50 |
+
])
|
| 51 |
+
# For r_small_factor = 0.99: r_small approx 0.0825, r_large approx 0.12625
|
| 52 |
+
# y_coords approx: [0.0825, 0.29125, 0.5, 0.70875, 0.9175]
|
| 53 |
+
|
| 54 |
+
# X-coordinates for rows with 6 circles (3 rows)
|
| 55 |
+
# Implies max radius of 1/(2*6) = 1/12 for x-dimension
|
| 56 |
+
x_coords_6 = np.linspace(1.0 / (2 * 6), 1.0 - 1.0 / (2 * 6), 6) # centers at 1/12, 3/12, ..., 11/12
|
| 57 |
+
|
| 58 |
+
# X-coordinates for rows with 4 circles (2 rows)
|
| 59 |
+
# This reverts to a non-staggered grid which empirically performed better in previous runs.
|
| 60 |
+
# The centers are at 1/8, 3/8, 5/8, 7/8.
|
| 61 |
+
x_coords_4 = np.linspace(1.0 / (2 * 4), 1.0 - 1.0 / (2 * 4), 4)
|
| 62 |
+
|
| 63 |
+
k = 0
|
| 64 |
+
# Rows with 6 circles: y_coords[0], y_coords[2], y_coords[4] (i.e., y=0.1, 0.5, 0.9)
|
| 65 |
+
for i in [0, 2, 4]:
|
| 66 |
+
for x_coord in x_coords_6:
|
| 67 |
+
centers[k, 0] = x_coord
|
| 68 |
+
centers[k, 1] = y_coords[i]
|
| 69 |
+
k += 1
|
| 70 |
+
|
| 71 |
+
# Rows with 4 circles: y_coords[1], y_coords[3] (i.e., y=0.3, 0.7)
|
| 72 |
+
for i in [1, 3]:
|
| 73 |
+
for x_coord in x_coords_4:
|
| 74 |
+
centers[k, 0] = x_coord
|
| 75 |
+
centers[k, 1] = y_coords[i]
|
| 76 |
+
k += 1
|
| 77 |
+
|
| 78 |
+
# The previous clipping was detrimental for configurations where circles
|
| 79 |
+
# are intended to be very close to the boundaries, e.g., for optimal grid packing.
|
| 80 |
+
# The compute_max_radii function already handles boundary conditions.
|
| 81 |
+
|
| 82 |
+
radii = compute_max_radii(centers)
|
| 83 |
+
return centers, radii
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def compute_max_radii(centers):
|
| 87 |
+
"""
|
| 88 |
+
Compute the maximum possible radii for each circle position
|
| 89 |
+
such that they don't overlap and stay within the unit square, using
|
| 90 |
+
an iterative approach to resolve overlaps.
|
| 91 |
+
|
| 92 |
+
Args:
|
| 93 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates
|
| 94 |
+
|
| 95 |
+
Returns:
|
| 96 |
+
np.array of shape (n) with radius of each circle
|
| 97 |
+
"""
|
| 98 |
+
n = centers.shape[0]
|
| 99 |
+
radii = np.zeros(n)
|
| 100 |
+
|
| 101 |
+
# Initialize radii to the maximum possible value limited by boundaries
|
| 102 |
+
for i in range(n):
|
| 103 |
+
x, y = centers[i]
|
| 104 |
+
radii[i] = min(x, y, 1 - x, 1 - y)
|
| 105 |
+
|
| 106 |
+
# Pre-compute distances for efficiency
|
| 107 |
+
dists = np.zeros((n, n))
|
| 108 |
+
for i in range(n):
|
| 109 |
+
for j in range(i + 1, n):
|
| 110 |
+
d = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
|
| 111 |
+
dists[i, j] = dists[j, i] = d
|
| 112 |
+
|
| 113 |
+
# Iteratively resolve overlaps by proportionally scaling down overlapping pairs.
|
| 114 |
+
# This is repeated until no significant changes occur, ensuring a stable solution.
|
| 115 |
+
for _ in range(500):
|
| 116 |
+
changed_in_pass = False
|
| 117 |
+
for i in range(n):
|
| 118 |
+
for j in range(i + 1, n):
|
| 119 |
+
dist = dists[i, j]
|
| 120 |
+
|
| 121 |
+
# If current radii cause an overlap (with a small tolerance)
|
| 122 |
+
if radii[i] + radii[j] > dist + 1e-12:
|
| 123 |
+
changed_in_pass = True
|
| 124 |
+
# Calculate the scaling factor to make them just touch
|
| 125 |
+
scale = dist / (radii[i] + radii[j])
|
| 126 |
+
radii[i] *= scale
|
| 127 |
+
radii[j] *= scale
|
| 128 |
+
|
| 129 |
+
# If a full pass resulted in no changes, the packing has stabilized
|
| 130 |
+
if not changed_in_pass:
|
| 131 |
+
break
|
| 132 |
+
|
| 133 |
+
return radii
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
# EVOLVE-BLOCK-END
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
# This part remains fixed (not evolved)
|
| 140 |
+
def run_packing():
|
| 141 |
+
"""Run the circle packing constructor for n=26"""
|
| 142 |
+
centers, radii = construct_packing()
|
| 143 |
+
# Calculate the sum of radii
|
| 144 |
+
sum_radii = np.sum(radii)
|
| 145 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/results/metrics.json
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 2.1289917762807655,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 2.1289917762807655,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.0833, 0.0833)\n centers[1] = (0.2500, 0.0833)\n centers[2] = (0.4167, 0.0833)\n centers[3] = (0.5833, 0.0833)\n centers[4] = (0.7500, 0.0833)\n centers[5] = (0.9167, 0.0833)\n centers[6] = (0.0833, 0.5000)\n centers[7] = (0.2500, 0.5000)\n centers[8] = (0.4167, 0.5000)\n centers[9] = (0.5833, 0.5000)\n centers[10] = (0.7500, 0.5000)\n centers[11] = (0.9167, 0.5000)\n centers[12] = (0.0833, 0.9167)\n centers[13] = (0.2500, 0.9167)\n centers[14] = (0.4167, 0.9167)\n centers[15] = (0.5833, 0.9167)\n centers[16] = (0.7500, 0.9167)\n centers[17] = (0.9167, 0.9167)\n centers[18] = (0.1250, 0.2907)\n centers[19] = (0.3750, 0.2907)\n centers[20] = (0.6250, 0.2907)\n centers[21] = (0.8750, 0.2907)\n centers[22] = (0.1250, 0.7073)\n centers[23] = (0.3750, 0.7073)\n centers[24] = (0.6250, 0.7073)\n centers[25] = (0.8750, 0.7073)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 2.1289917762807655
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 0.002625906839966774,
|
| 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 |
+
"extra_npz_load_error": null,
|
| 23 |
+
"metrics_json_load_error": null,
|
| 24 |
+
"data_missing_error": null,
|
| 25 |
+
"num_circles_actual": -1,
|
| 26 |
+
"error_num_circles_mismatch": null,
|
| 27 |
+
"packing_efficiency_error": null,
|
| 28 |
+
"center_of_mass_deviation_error": null,
|
| 29 |
+
"violation_magnitudes_error": null,
|
| 30 |
+
"radii_distribution_error": null,
|
| 31 |
+
"total_packed_area_ratio": 0.6264467883159194,
|
| 32 |
+
"avg_min_edge_proximity": 0.07837211116868853,
|
| 33 |
+
"min_min_edge_proximity": 0.0,
|
| 34 |
+
"center_of_mass_deviation": 0.00030769230769239764,
|
| 35 |
+
"center_of_mass_x": 0.5,
|
| 36 |
+
"center_of_mass_y": 0.4996923076923076,
|
| 37 |
+
"max_boundary_violation": 0.0,
|
| 38 |
+
"boundary_num_violations": 0,
|
| 39 |
+
"boundary_total_penetration_distance": 0.0,
|
| 40 |
+
"boundary_max_penetration_distance": 0.0,
|
| 41 |
+
"max_overlap_magnitude": 1.1102230246251565e-16,
|
| 42 |
+
"overlap_num_violations": 0,
|
| 43 |
+
"overlap_total_distance": 0.0,
|
| 44 |
+
"radii_mean": 0.08188429908772175,
|
| 45 |
+
"radii_std_dev": 0.03105405121890597,
|
| 46 |
+
"radii_min": 0.030435344253206726,
|
| 47 |
+
"radii_max": 0.12791369446168155,
|
| 48 |
+
"min_separation_ratio_to_touching": -6.661338147750936e-16,
|
| 49 |
+
"avg_separation_ratio_to_touching": 2.613472560067026
|
| 50 |
+
},
|
| 51 |
+
"auxiliary_descriptions": {
|
| 52 |
+
"total_packed_area_ratio": "The sum of the areas of all circles (`sum(pi * r^2)`) as a ratio of the unit square's area (1.0). A value closer to 1.0 indicates a more efficient packing in terms of area covered. Useful for understanding how much of the available space is utilized, especially when the primary metric (`sum(radii)`) might favor different radius distributions.",
|
| 53 |
+
"avg_min_edge_proximity": "The average of the minimum distances from each circle's edge to the closest boundary of the unit square. This metric indicates, on average, how tightly packed the circles are against the container's walls. Smaller values suggest better utilization of the boundaries, potentially leading to higher overall packing.",
|
| 54 |
+
"min_min_edge_proximity": "The absolute minimum distance found among all circles, from any circle's edge to the closest boundary of the unit square. A value close to zero (or negative, if there's a slight penetration) indicates that at least one circle is very close to or touching a boundary. This metric helps identify individual circles that are pushed hard against the edges.",
|
| 55 |
+
"radii_mean": "The average radius of all packed circles. Tracks the general size of circles being placed.",
|
| 56 |
+
"radii_std_dev": "The standard deviation of the radii of all packed circles. A higher standard deviation suggests a more diverse set of circle sizes, while a lower one indicates more uniform sizes. This can reveal different packing strategies (e.g., solutions favoring many small circles vs. fewer large ones).",
|
| 57 |
+
"radii_min": "The minimum radius among all packed circles.",
|
| 58 |
+
"radii_max": "The maximum radius among all packed circles.",
|
| 59 |
+
"center_of_mass_deviation": "The Euclidean distance of the combined center of mass of all circles from the center of the unit square (0.5, 0.5). A lower value indicates a more centrally balanced packing. This can be useful to identify solutions that cluster circles to one side.",
|
| 60 |
+
"center_of_mass_x": "The x-coordinate of the combined center of mass of all circles.",
|
| 61 |
+
"center_of_mass_y": "The y-coordinate of the combined center of mass of all circles.",
|
| 62 |
+
"max_boundary_violation": "The maximum amount by which any circle extends beyond the unit square boundaries. For a valid solution, this should be 0.0. A positive value indicates an invalid solution.",
|
| 63 |
+
"boundary_num_violations": "The count of individual boundary penetrations (a single circle can penetrate multiple boundaries). For a valid solution, this should be 0.",
|
| 64 |
+
"boundary_total_penetration_distance": "The sum of all distances by which circles penetrate the boundaries. For a valid solution, this should be 0.0.",
|
| 65 |
+
"boundary_max_penetration_distance": "The maximum penetration distance by any circle into any boundary. For a valid solution, this should be 0.0.",
|
| 66 |
+
"max_overlap_magnitude": "The maximum depth of overlap between any two circles. For a valid solution, this should be 0.0. A positive value indicates an invalid solution.",
|
| 67 |
+
"overlap_num_violations": "The count of overlapping pairs of circles. For a valid solution, this should be 0.",
|
| 68 |
+
"overlap_total_distance": "The sum of overlap depths for all overlapping pairs of circles. For a valid solution, this should be 0.0.",
|
| 69 |
+
"extra_npz_load_error": "Records error message if `extra.npz` (containing circle data) cannot be loaded.",
|
| 70 |
+
"metrics_json_load_error": "Records error message if `metrics.json` (containing primary score and expected circle count) cannot be loaded.",
|
| 71 |
+
"data_missing_error": "Indicates if `centers` or `radii` data is missing or empty after loading `extra.npz`.",
|
| 72 |
+
"num_circles_actual": "The actual number of circles found in the loaded data (should be 26 for valid solutions).",
|
| 73 |
+
"error_num_circles_mismatch": "Records an error message if the actual number of circles does not match the expected number (26), which would typically lead to an invalid primary score.",
|
| 74 |
+
"packing_efficiency_error": "Records error message during packing efficiency calculations.",
|
| 75 |
+
"center_of_mass_deviation_error": "Records error message during center of mass deviation calculations.",
|
| 76 |
+
"violation_magnitudes_error": "Records error message during violation magnitudes calculations.",
|
| 77 |
+
"radii_distribution_error": "Records error message during radii distribution calculations.",
|
| 78 |
+
"evaluate_aux_overall_error": "Catches any unhandled exceptions within the `evaluate_aux` function, providing a top-level error indicator.",
|
| 79 |
+
"total_area_covered": "The sum of the areas of all 26 circles. This metric provides a direct measure of the total space occupied within the unit square. Higher values indicate more efficient use of space. Since the primary score is `sum(radii)`, `total_area_covered` (proportional to `sum(radii^2)`) offers a slightly different perspective on packing density.",
|
| 80 |
+
"min_boundary_distance": "The minimum distance from the edge of any circle to any of the four boundaries of the unit square. A smaller value indicates that circles are packed closer to the container's edges, suggesting more efficient use of the available packing area and potentially denser solutions.",
|
| 81 |
+
"avg_boundary_distance": "The average minimum distance from the edge of each circle to its closest boundary. This provides a smoother measure of how well, on average, the circles are utilizing the space near the boundaries.",
|
| 82 |
+
"radii_median": "The median radius among all circles. Provides a measure of the central tendency of circle sizes, less sensitive to outliers than the mean.",
|
| 83 |
+
"avg_boundary_proximity": "The average of the minimum distances from each circle's edge to any of the four boundaries of the unit square. Lower values indicate that circles are placed closer to the edges, suggesting better utilization of the square's perimeter and potentially more efficient packing.",
|
| 84 |
+
"min_separation_ratio_to_touching": "The smallest `(distance - sum_of_radii) / sum_of_radii` ratio among all pairs of circles. A value close to 0 (and positive) indicates that at least one pair of circles is very close to touching without overlapping. A negative value indicates overlap. This metric measures the \"tightness\" of the closest non-overlapping pair.",
|
| 85 |
+
"avg_separation_ratio_to_touching": "The average of `(distance - sum_of_radii) / sum_of_radii` for all pairs of circles. Provides a general sense of how much empty space or separation exists between circles on average."
|
| 86 |
+
},
|
| 87 |
+
"timestamp": 1770342259.3140066,
|
| 88 |
+
"generation": 31
|
| 89 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/best/rewrite.txt
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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)
|
| 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 |
+
"""
|
| 17 |
+
# Initialize arrays for 26 circles
|
| 18 |
+
n = 26
|
| 19 |
+
centers = np.zeros((n, 2))
|
| 20 |
+
|
| 21 |
+
# Strategy: Place circles in an uneven grid pattern (3 rows of 6, 2 rows of 4)
|
| 22 |
+
# This accounts for all 26 circles and aims for better space utilization.
|
| 23 |
+
|
| 24 |
+
# Y-coordinates for 5 logical rows
|
| 25 |
+
# The y-coordinates are calculated based on 'ideal' radii for 6-circle rows (r_small)
|
| 26 |
+
# and 4-circle rows (r_large), assuming a vertical tangency model as a baseline.
|
| 27 |
+
# The constraint 6*r_small + 4*r_large = 1.0 defines how these radii fill the vertical space.
|
| 28 |
+
# We revert to the default r_small = 1/12 and r_large = 1/8, which previously yielded good results.
|
| 29 |
+
r_small = 1.0 / 12.0
|
| 30 |
+
r_large = 1.0 / 8.0
|
| 31 |
+
|
| 32 |
+
# Baseline y-coordinates based on ideal vertical tangency
|
| 33 |
+
y_coords = np.array([
|
| 34 |
+
r_small, # Bottom 6-circle row (y_0)
|
| 35 |
+
r_small + r_small + r_large, # First 4-circle row (y_1)
|
| 36 |
+
r_small + r_small + r_large + r_large + r_small, # Middle 6-circle row (y_2)
|
| 37 |
+
r_small + r_small + r_large + r_large + r_small + r_small + r_large, # Second 4-circle row (y_3)
|
| 38 |
+
1.0 - r_small # Top 6-circle row (y_4) - ensures symmetry and total height
|
| 39 |
+
])
|
| 40 |
+
|
| 41 |
+
# New tunable parameter: Micro-Vertical Shift for 4-Circle Rows (Recommendation 5)
|
| 42 |
+
# This slightly perturbs the y-coordinates of the 4-circle rows to explore non-ideal
|
| 43 |
+
# vertical tangency arrangements that might lead to larger overall radii.
|
| 44 |
+
y_shift_4_rows = -0.001 # Small negative shift
|
| 45 |
+
|
| 46 |
+
# Apply the shift to the two 4-circle rows
|
| 47 |
+
y_coords[1] += y_shift_4_rows
|
| 48 |
+
y_coords[3] += y_shift_4_rows
|
| 49 |
+
|
| 50 |
+
# X-coordinates for rows with 6 circles (3 rows)
|
| 51 |
+
# Fixed based on previous successful iterations.
|
| 52 |
+
x_coords_6 = np.linspace(1.0 / (2 * 6), 1.0 - 1.0 / (2 * 6), 6)
|
| 53 |
+
|
| 54 |
+
# X-coordinates for rows with 4 circles (2 rows)
|
| 55 |
+
# Fixed based on previous successful iterations (non-staggered relative to the boundaries).
|
| 56 |
+
x_coords_4 = np.linspace(1.0 / (2 * 4), 1.0 - 1.0 / (2 * 4), 4)
|
| 57 |
+
|
| 58 |
+
k = 0
|
| 59 |
+
# Rows with 6 circles: y_coords[0], y_coords[2], y_coords[4]
|
| 60 |
+
for i in [0, 2, 4]:
|
| 61 |
+
for x_coord in x_coords_6:
|
| 62 |
+
centers[k, 0] = x_coord
|
| 63 |
+
centers[k, 1] = y_coords[i]
|
| 64 |
+
k += 1
|
| 65 |
+
|
| 66 |
+
# Rows with 4 circles: y_coords[1], y_coords[3]
|
| 67 |
+
for i in [1, 3]:
|
| 68 |
+
for x_coord in x_coords_4:
|
| 69 |
+
centers[k, 0] = x_coord
|
| 70 |
+
centers[k, 1] = y_coords[i]
|
| 71 |
+
k += 1
|
| 72 |
+
|
| 73 |
+
radii = compute_max_radii(centers)
|
| 74 |
+
return centers, radii
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def compute_max_radii(centers):
|
| 78 |
+
"""
|
| 79 |
+
Compute the maximum possible radii for each circle position
|
| 80 |
+
such that they don't overlap and stay within the unit square, using
|
| 81 |
+
an iterative approach to resolve overlaps.
|
| 82 |
+
|
| 83 |
+
Args:
|
| 84 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates
|
| 85 |
+
|
| 86 |
+
Returns:
|
| 87 |
+
np.array of shape (n) with radius of each circle
|
| 88 |
+
"""
|
| 89 |
+
n = centers.shape[0]
|
| 90 |
+
radii = np.zeros(n)
|
| 91 |
+
|
| 92 |
+
# Initialize radii to the maximum possible value limited by boundaries
|
| 93 |
+
for i in range(n):
|
| 94 |
+
x, y = centers[i]
|
| 95 |
+
radii[i] = min(x, y, 1 - x, 1 - y)
|
| 96 |
+
|
| 97 |
+
# Pre-compute distances for efficiency
|
| 98 |
+
dists = np.zeros((n, n))
|
| 99 |
+
for i in range(n):
|
| 100 |
+
for j in range(i + 1, n):
|
| 101 |
+
d = np.sqrt(np.sum((centers[i] - centers[j]) ** 2))
|
| 102 |
+
dists[i, j] = dists[j, i] = d
|
| 103 |
+
|
| 104 |
+
# Iteratively resolve overlaps by proportionally scaling down overlapping pairs.
|
| 105 |
+
# This is repeated until no significant changes occur, ensuring a stable solution.
|
| 106 |
+
for _ in range(500):
|
| 107 |
+
changed_in_pass = False
|
| 108 |
+
for i in range(n):
|
| 109 |
+
for j in range(i + 1, n):
|
| 110 |
+
dist = dists[i, j]
|
| 111 |
+
|
| 112 |
+
# If current radii cause an overlap (with a small tolerance)
|
| 113 |
+
if radii[i] + radii[j] > dist + 1e-12:
|
| 114 |
+
changed_in_pass = True
|
| 115 |
+
# Calculate the scaling factor to make them just touch
|
| 116 |
+
scale = dist / (radii[i] + radii[j])
|
| 117 |
+
radii[i] *= scale
|
| 118 |
+
radii[j] *= scale
|
| 119 |
+
|
| 120 |
+
# If a full pass resulted in no changes, the packing has stabilized
|
| 121 |
+
if not changed_in_pass:
|
| 122 |
+
break
|
| 123 |
+
|
| 124 |
+
return radii
|
| 125 |
+
# EVOLVE-BLOCK-END
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/eval_agent_memory/__pycache__/auxiliary_metrics.cpython-313.pyc
ADDED
|
Binary file (7.87 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (3.14 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results/job_log.err
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/home/tengxiao/pj/ShinkaEvolve/.venv/lib/python3.13/site-packages/instructor/providers/gemini/client.py:5: FutureWarning:
|
| 2 |
+
|
| 3 |
+
All support for the `google.generativeai` package has ended. It will no longer be receiving
|
| 4 |
+
updates or bug fixes. Please switch to the `google.genai` package as soon as possible.
|
| 5 |
+
See README for more details:
|
| 6 |
+
|
| 7 |
+
https://github.com/google-gemini/deprecated-generative-ai-python/blob/main/README.md
|
| 8 |
+
|
| 9 |
+
import google.generativeai as genai # type: ignore[import-not-found]
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results/job_log.out
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Evaluating program: examples/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/main.py
|
| 2 |
+
Saving results to: examples/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results
|
| 3 |
+
Run 1/1 completed in 0.00 seconds
|
| 4 |
+
Detailed packing data saved to examples/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results/extra.npz
|
| 5 |
+
Correctness and error status saved to examples/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results/correct.json
|
| 6 |
+
Metrics saved to examples/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results/metrics.json
|
| 7 |
+
Evaluation and Validation completed successfully.
|
| 8 |
+
Metrics:
|
| 9 |
+
combined_score: 0.9597642169962064
|
| 10 |
+
public: {'centers_str': ' centers[0] = (0.5000, 0.5000)\n centers[1] = (0.8000, 0.5000)\n centers[2] = (0.7121, 0.7121)\n centers[3] = (0.5000, 0.8000)\n centers[4] = (0.2879, 0.7121)\n centers[5] = (0.2000, 0.5000)\n centers[6] = (0.2879, 0.2879)\n centers[7] = (0.5000, 0.2000)\n centers[8] = (0.7121, 0.2879)\n centers[9] = (0.9900, 0.5000)\n centers[10] = (0.9900, 0.7679)\n centers[11] = (0.9900, 0.9900)\n centers[12] = (0.7679, 0.9900)\n centers[13] = (0.5000, 0.9900)\n centers[14] = (0.2321, 0.9900)\n centers[15] = (0.0100, 0.9900)\n centers[16] = (0.0100, 0.7679)\n centers[17] = (0.0100, 0.5000)\n centers[18] = (0.0100, 0.2321)\n centers[19] = (0.0100, 0.0100)\n centers[20] = (0.2321, 0.0100)\n centers[21] = (0.5000, 0.0100)\n centers[22] = (0.7679, 0.0100)\n centers[23] = (0.9900, 0.0100)\n centers[24] = (0.9900, 0.2321)\n centers[25] = (0.0100, 0.0100)', 'num_circles': 26}
|
| 11 |
+
private: {'reported_sum_of_radii': 0.9597642169962064}
|
| 12 |
+
execution_time_mean: 0.002187669277191162
|
| 13 |
+
execution_time_std: 0.0
|
| 14 |
+
num_valid_runs: 1
|
| 15 |
+
num_invalid_runs: 0
|
| 16 |
+
all_validation_errors: []
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_0/results/metrics.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 0.9597642169962064,
|
| 3 |
+
"public": {
|
| 4 |
+
"centers_str": " centers[0] = (0.5000, 0.5000)\n centers[1] = (0.8000, 0.5000)\n centers[2] = (0.7121, 0.7121)\n centers[3] = (0.5000, 0.8000)\n centers[4] = (0.2879, 0.7121)\n centers[5] = (0.2000, 0.5000)\n centers[6] = (0.2879, 0.2879)\n centers[7] = (0.5000, 0.2000)\n centers[8] = (0.7121, 0.2879)\n centers[9] = (0.9900, 0.5000)\n centers[10] = (0.9900, 0.7679)\n centers[11] = (0.9900, 0.9900)\n centers[12] = (0.7679, 0.9900)\n centers[13] = (0.5000, 0.9900)\n centers[14] = (0.2321, 0.9900)\n centers[15] = (0.0100, 0.9900)\n centers[16] = (0.0100, 0.7679)\n centers[17] = (0.0100, 0.5000)\n centers[18] = (0.0100, 0.2321)\n centers[19] = (0.0100, 0.0100)\n centers[20] = (0.2321, 0.0100)\n centers[21] = (0.5000, 0.0100)\n centers[22] = (0.7679, 0.0100)\n centers[23] = (0.9900, 0.0100)\n centers[24] = (0.9900, 0.2321)\n centers[25] = (0.0100, 0.0100)",
|
| 5 |
+
"num_circles": 26
|
| 6 |
+
},
|
| 7 |
+
"private": {
|
| 8 |
+
"reported_sum_of_radii": 0.9597642169962064
|
| 9 |
+
},
|
| 10 |
+
"execution_time_mean": 0.002187669277191162,
|
| 11 |
+
"execution_time_std": 0.0,
|
| 12 |
+
"num_valid_runs": 1,
|
| 13 |
+
"num_invalid_runs": 0,
|
| 14 |
+
"all_validation_errors": []
|
| 15 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_1/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (3.08 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_1/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_1/results/metrics.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 1.1250000000000004,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 1.1250000000000004,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.2000, 0.2000)\n centers[1] = (0.2000, 0.4000)\n centers[2] = (0.2000, 0.6000)\n centers[3] = (0.2000, 0.8000)\n centers[4] = (0.4000, 0.2000)\n centers[5] = (0.4000, 0.4000)\n centers[6] = (0.4000, 0.6000)\n centers[7] = (0.4000, 0.8000)\n centers[8] = (0.6000, 0.2000)\n centers[9] = (0.6000, 0.4000)\n centers[10] = (0.6000, 0.6000)\n centers[11] = (0.6000, 0.8000)\n centers[12] = (0.8000, 0.2000)\n centers[13] = (0.8000, 0.4000)\n centers[14] = (0.8000, 0.6000)\n centers[15] = (0.8000, 0.8000)\n centers[16] = (0.0500, 0.0500)\n centers[17] = (0.0500, 0.9500)\n centers[18] = (0.9500, 0.0500)\n centers[19] = (0.9500, 0.9500)\n centers[20] = (0.3333, 0.9500)\n centers[21] = (0.6667, 0.9500)\n centers[22] = (0.3333, 0.0500)\n centers[23] = (0.6667, 0.0500)\n centers[24] = (0.0500, 0.5000)\n centers[25] = (0.9500, 0.5000)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 1.1250000000000004
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 0.0022673793137073517,
|
| 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": 1770338931.8901668,
|
| 24 |
+
"generation": 1
|
| 25 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (6.75 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/edit.diff
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--- a/original.py
|
| 2 |
+
+++ b/original.py
|
| 3 |
+
@@ -1,190 +1,208 @@
|
| 4 |
+
# EVOLVE-BLOCK-START
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
def construct_packing():
|
| 8 |
+
"""
|
| 9 |
+
Construct an arrangement of 26 circles in a unit square
|
| 10 |
+
using a force-directed simulation to maximize the sum of their radii.
|
| 11 |
+
|
| 12 |
+
This algorithm starts with an initial circle placement and iteratively
|
| 13 |
+
adjusts their positions based on repulsive forces between overlapping
|
| 14 |
+
circles and from the square boundaries, while continuously maximizing
|
| 15 |
+
their radii for the current positions. This allows the system to
|
| 16 |
+
settle into a more optimal, tightly packed configuration.
|
| 17 |
+
|
| 18 |
+
Returns:
|
| 19 |
+
Tuple of (centers, radii)
|
| 20 |
+
centers: np.array of shape (26, 2) with (x, y) coordinates
|
| 21 |
+
radii: np.array of shape (26) with radius of each circle
|
| 22 |
+
"""
|
| 23 |
+
n = 26
|
| 24 |
+
centers = np.zeros((n, 2))
|
| 25 |
+
|
| 26 |
+
- # Initial placement: a slightly perturbed grid of 5x5 circles + 1 extra
|
| 27 |
+
- # This provides a reasonable starting distribution for the simulation.
|
| 28 |
+
- grid_side = 5
|
| 29 |
+
- spacing = 1.0 / (grid_side + 1) # Distribute centers from 1/6 to 5/6
|
| 30 |
+
-
|
| 31 |
+
+ # Initial placement: A 4-fold symmetric pattern (D4 symmetry).
|
| 32 |
+
+ # For N=26, we use 3 seed points generating 8 points each (24),
|
| 33 |
+
+ # plus 2 points on the main diagonal y=x.
|
| 34 |
+
+ # This avoids the grid-like local minimum and explores more complex structures.
|
| 35 |
+
count = 0
|
| 36 |
+
- for i in range(grid_side):
|
| 37 |
+
- for j in range(grid_side):
|
| 38 |
+
- if count < n: # Place up to 25 circles in the 5x5 grid
|
| 39 |
+
- centers[count] = [(i + 1) * spacing, (j + 1) * spacing]
|
| 40 |
+
+
|
| 41 |
+
+ # 1. Two points on the main diagonal
|
| 42 |
+
+ diag_dist = 0.25
|
| 43 |
+
+ centers[count] = [0.5 - diag_dist, 0.5 - diag_dist]; count += 1
|
| 44 |
+
+ centers[count] = [0.5 + diag_dist, 0.5 + diag_dist]; count += 1
|
| 45 |
+
+
|
| 46 |
+
+ # 2. Three seed points in the fundamental domain wedge (0.5 <= y <= x <= 1)
|
| 47 |
+
+ seed_points = np.array([
|
| 48 |
+
+ [0.8, 0.6],
|
| 49 |
+
+ [0.9, 0.7],
|
| 50 |
+
+ [0.7, 0.55]
|
| 51 |
+
+ ])
|
| 52 |
+
+
|
| 53 |
+
+ for p in seed_points:
|
| 54 |
+
+ x, y = p[0], p[1]
|
| 55 |
+
+ # Generate up to 8 points by D4 symmetry operations
|
| 56 |
+
+ base_points = {(x, y), (y, x)}
|
| 57 |
+
+ all_points = set()
|
| 58 |
+
+ for pt in base_points:
|
| 59 |
+
+ px, py = pt
|
| 60 |
+
+ all_points.add((px, py))
|
| 61 |
+
+ all_points.add((1 - px, py))
|
| 62 |
+
+ all_points.add((px, 1 - py))
|
| 63 |
+
+ all_points.add((1 - px, 1 - py))
|
| 64 |
+
+
|
| 65 |
+
+ for point in sorted(list(all_points)): # sorted for determinism
|
| 66 |
+
+ if count < n:
|
| 67 |
+
+ centers[count] = point
|
| 68 |
+
count += 1
|
| 69 |
+
-
|
| 70 |
+
- # Place the 26th circle. For 26, one remains after 5x5 grid (25 circles).
|
| 71 |
+
- if count < n:
|
| 72 |
+
- centers[count] = [0.5, 0.5] # Place it at the center of the square
|
| 73 |
+
- count += 1
|
| 74 |
+
|
| 75 |
+
# Add small random perturbation to initial positions to break symmetry.
|
| 76 |
+
# This helps the optimization escape local minima and explore more diverse configurations.
|
| 77 |
+
centers += np.random.uniform(-0.02, 0.02, centers.shape)
|
| 78 |
+
|
| 79 |
+
# Ensure initial centers are within the [0,1] bounds
|
| 80 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 81 |
+
|
| 82 |
+
# --- Force-directed simulation parameters ---
|
| 83 |
+
- num_iterations_total = 2500 # Total number of simulation steps
|
| 84 |
+
+ num_iterations_total = 4000 # Total number of simulation steps
|
| 85 |
+
initial_learning_rate = 0.01 # Initial step size for moving centers
|
| 86 |
+
- k_rep = 0.05 # Strength of circle-circle repulsion
|
| 87 |
+
- k_wall = 0.1 # Strength of wall repulsion
|
| 88 |
+
+ k_rep = 0.08 # Strength of circle-circle repulsion
|
| 89 |
+
+ k_wall = 0.09 # Strength of wall repulsion
|
| 90 |
+
|
| 91 |
+
# The learning rate decays over time, simulating an annealing process.
|
| 92 |
+
# This allows for larger movements initially and finer adjustments later.
|
| 93 |
+
current_learning_rate = initial_learning_rate
|
| 94 |
+
|
| 95 |
+
for iteration in range(num_iterations_total):
|
| 96 |
+
# 1. Compute radii for current centers.
|
| 97 |
+
# This function aims to maximize radii such that no circles overlap
|
| 98 |
+
# and all are within bounds for the given center positions.
|
| 99 |
+
radii = compute_max_radii_iterative(centers)
|
| 100 |
+
|
| 101 |
+
# 2. Calculate forces on centers.
|
| 102 |
+
# These forces push circles apart if they overlap or are too close to walls.
|
| 103 |
+
forces = np.zeros((n, 2))
|
| 104 |
+
|
| 105 |
+
for i in range(n):
|
| 106 |
+
# Wall repulsion: push circles away from boundaries if their radius extends beyond.
|
| 107 |
+
x, y = centers[i]
|
| 108 |
+
r = radii[i]
|
| 109 |
+
|
| 110 |
+
if x - r < 0: forces[i, 0] += k_wall * (r - x)
|
| 111 |
+
if x + r > 1: forces[i, 0] -= k_wall * ((x + r) - 1)
|
| 112 |
+
if y - r < 0: forces[i, 1] += k_wall * (r - y)
|
| 113 |
+
if y + r > 1: forces[i, 1] -= k_wall * ((y + r) - 1)
|
| 114 |
+
|
| 115 |
+
# Circle-circle repulsion: push overlapping circles apart.
|
| 116 |
+
for j in range(i + 1, n):
|
| 117 |
+
vec = centers[i] - centers[j]
|
| 118 |
+
dist = np.linalg.norm(vec)
|
| 119 |
+
|
| 120 |
+
min_separation = radii[i] + radii[j]
|
| 121 |
+
if dist < min_separation + 1e-9: # Overlap detected (with small tolerance)
|
| 122 |
+
overlap_amount = min_separation - dist
|
| 123 |
+
if dist > 1e-9: # Avoid division by zero if centers are too close
|
| 124 |
+
force_magnitude = k_rep * overlap_amount / dist
|
| 125 |
+
forces[i] += force_magnitude * vec
|
| 126 |
+
forces[j] -= force_magnitude * vec
|
| 127 |
+
else: # Centers are virtually identical, push in opposite random directions
|
| 128 |
+
random_vec = np.random.uniform(-1, 1, 2)
|
| 129 |
+
random_vec /= np.linalg.norm(random_vec)
|
| 130 |
+
forces[i] += k_rep * overlap_amount * random_vec
|
| 131 |
+
forces[j] -= k_rep * overlap_amount * random_vec
|
| 132 |
+
|
| 133 |
+
# 3. Update centers based on calculated forces.
|
| 134 |
+
centers += current_learning_rate * forces
|
| 135 |
+
|
| 136 |
+
# 4. Decay learning rate for annealing.
|
| 137 |
+
current_learning_rate = initial_learning_rate * (1 - iteration / num_iterations_total)
|
| 138 |
+
current_learning_rate = max(current_learning_rate, initial_learning_rate * 0.01) # Maintain a minimum step size
|
| 139 |
+
|
| 140 |
+
# 5. Clip centers to ensure they stay within the unit square.
|
| 141 |
+
# This is important for `compute_max_radii_iterative` to correctly calculate boundary limits.
|
| 142 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 143 |
+
|
| 144 |
+
# Final computation of radii after the simulation has settled.
|
| 145 |
+
final_radii = compute_max_radii_iterative(centers)
|
| 146 |
+
|
| 147 |
+
return centers, final_radii
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def compute_max_radii_iterative(centers):
|
| 151 |
+
"""
|
| 152 |
+
Computes the maximum possible radii for given circle centers such that all
|
| 153 |
+
circles are disjoint and contained within the unit square. This is an
|
| 154 |
+
iterative relaxation method that ensures convergence by repeatedly
|
| 155 |
+
checking and resolving overlaps.
|
| 156 |
+
|
| 157 |
+
Args:
|
| 158 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates of circle centers.
|
| 159 |
+
|
| 160 |
+
Returns:
|
| 161 |
+
np.array of shape (n) with the radius of each circle.
|
| 162 |
+
"""
|
| 163 |
+
n = centers.shape[0]
|
| 164 |
+
radii = np.zeros(n)
|
| 165 |
+
|
| 166 |
+
# Initialize radii based on the maximum possible distance to the nearest boundary.
|
| 167 |
+
# This ensures circles start as large as possible given only boundary constraints.
|
| 168 |
+
for i in range(n):
|
| 169 |
+
x, y = centers[i]
|
| 170 |
+
radii[i] = min(x, 1 - x, y, 1 - y)
|
| 171 |
+
radii[i] = max(radii[i], 1e-9) # Ensure a small positive radius to start.
|
| 172 |
+
|
| 173 |
+
# Iteratively adjust radii to prevent overlaps between circles.
|
| 174 |
+
# The loop runs for a fixed number of iterations to allow changes to propagate
|
| 175 |
+
# through the system until a stable state (or near-stable) is reached.
|
| 176 |
+
max_radii_solver_iterations = 200
|
| 177 |
+
|
| 178 |
+
for _ in range(max_radii_solver_iterations):
|
| 179 |
+
changed = False
|
| 180 |
+
for i in range(n):
|
| 181 |
+
# Re-check boundary conditions as radii might have been adjusted for overlaps.
|
| 182 |
+
x, y = centers[i]
|
| 183 |
+
boundary_limit = min(x, 1 - x, y, 1 - y)
|
| 184 |
+
if radii[i] > boundary_limit + 1e-9: # If radius exceeds boundary limit
|
| 185 |
+
radii[i] = boundary_limit
|
| 186 |
+
changed = True
|
| 187 |
+
|
| 188 |
+
# Check for overlaps with all other circles.
|
| 189 |
+
for j in range(n):
|
| 190 |
+
if i == j: continue
|
| 191 |
+
|
| 192 |
+
dist = np.linalg.norm(centers[i] - centers[j])
|
| 193 |
+
|
| 194 |
+
# If circles i and j overlap (with a small tolerance)
|
| 195 |
+
if radii[i] + radii[j] > dist + 1e-9:
|
| 196 |
+
sum_current_radii = radii[i] + radii[j]
|
| 197 |
+
if sum_current_radii > 1e-9: # Avoid division by zero
|
| 198 |
+
# Scale both radii proportionally so they just touch.
|
| 199 |
+
scale = dist / sum_current_radii
|
| 200 |
+
radii[i] *= scale
|
| 201 |
+
radii[j] *= scale
|
| 202 |
+
changed = True
|
| 203 |
+
else: # Both radii are near zero and centers are close. Assign tiny non-overlapping radii.
|
| 204 |
+
radii[i] = dist / 2.0 - 1e-10
|
| 205 |
+
radii[j] = dist / 2.0 - 1e-10
|
| 206 |
+
radii[i] = max(radii[i], 1e-9)
|
| 207 |
+
radii[j] = max(radii[j], 1e-9)
|
| 208 |
+
changed = True
|
| 209 |
+
if not changed:
|
| 210 |
+
break # If no radii were adjusted in an iteration, we've converged.
|
| 211 |
+
|
| 212 |
+
# Final pass to ensure all radii are non-negative and strictly within boundary limits.
|
| 213 |
+
for i in range(n):
|
| 214 |
+
x, y = centers[i]
|
| 215 |
+
radii[i] = max(0.0, radii[i]) # Radii must be non-negative.
|
| 216 |
+
radii[i] = min(radii[i], x, 1 - x, y, 1 - y) # Final clamp to boundaries.
|
| 217 |
+
|
| 218 |
+
return radii
|
| 219 |
+
# EVOLVE-BLOCK-END
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
# This part remains fixed (not evolved)
|
| 223 |
+
def run_packing():
|
| 224 |
+
"""Run the circle packing constructor for n=26"""
|
| 225 |
+
centers, radii = construct_packing()
|
| 226 |
+
# Calculate the sum of radii
|
| 227 |
+
sum_radii = np.sum(radii)
|
| 228 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/main.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
def construct_packing():
|
| 5 |
+
"""
|
| 6 |
+
Construct an arrangement of 26 circles in a unit square
|
| 7 |
+
using a force-directed simulation to maximize the sum of their radii.
|
| 8 |
+
|
| 9 |
+
This algorithm starts with an initial circle placement and iteratively
|
| 10 |
+
adjusts their positions based on repulsive forces between overlapping
|
| 11 |
+
circles and from the square boundaries, while continuously maximizing
|
| 12 |
+
their radii for the current positions. This allows the system to
|
| 13 |
+
settle into a more optimal, tightly packed configuration.
|
| 14 |
+
|
| 15 |
+
Returns:
|
| 16 |
+
Tuple of (centers, radii)
|
| 17 |
+
centers: np.array of shape (26, 2) with (x, y) coordinates
|
| 18 |
+
radii: np.array of shape (26) with radius of each circle
|
| 19 |
+
"""
|
| 20 |
+
n = 26
|
| 21 |
+
centers = np.zeros((n, 2))
|
| 22 |
+
|
| 23 |
+
# Initial placement: A 4-fold symmetric pattern (D4 symmetry).
|
| 24 |
+
# For N=26, we use 3 seed points generating 8 points each (24),
|
| 25 |
+
# plus 2 points on the main diagonal y=x.
|
| 26 |
+
# This avoids the grid-like local minimum and explores more complex structures.
|
| 27 |
+
count = 0
|
| 28 |
+
|
| 29 |
+
# 1. Two points on the main diagonal
|
| 30 |
+
diag_dist = 0.25
|
| 31 |
+
centers[count] = [0.5 - diag_dist, 0.5 - diag_dist]; count += 1
|
| 32 |
+
centers[count] = [0.5 + diag_dist, 0.5 + diag_dist]; count += 1
|
| 33 |
+
|
| 34 |
+
# 2. Three seed points in the fundamental domain wedge (0.5 <= y <= x <= 1)
|
| 35 |
+
seed_points = np.array([
|
| 36 |
+
[0.8, 0.6],
|
| 37 |
+
[0.9, 0.7],
|
| 38 |
+
[0.7, 0.55]
|
| 39 |
+
])
|
| 40 |
+
|
| 41 |
+
for p in seed_points:
|
| 42 |
+
x, y = p[0], p[1]
|
| 43 |
+
# Generate up to 8 points by D4 symmetry operations
|
| 44 |
+
base_points = {(x, y), (y, x)}
|
| 45 |
+
all_points = set()
|
| 46 |
+
for pt in base_points:
|
| 47 |
+
px, py = pt
|
| 48 |
+
all_points.add((px, py))
|
| 49 |
+
all_points.add((1 - px, py))
|
| 50 |
+
all_points.add((px, 1 - py))
|
| 51 |
+
all_points.add((1 - px, 1 - py))
|
| 52 |
+
|
| 53 |
+
for point in sorted(list(all_points)): # sorted for determinism
|
| 54 |
+
if count < n:
|
| 55 |
+
centers[count] = point
|
| 56 |
+
count += 1
|
| 57 |
+
|
| 58 |
+
# Add small random perturbation to initial positions to break symmetry.
|
| 59 |
+
# This helps the optimization escape local minima and explore more diverse configurations.
|
| 60 |
+
centers += np.random.uniform(-0.02, 0.02, centers.shape)
|
| 61 |
+
|
| 62 |
+
# Ensure initial centers are within the [0,1] bounds
|
| 63 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 64 |
+
|
| 65 |
+
# --- Force-directed simulation parameters ---
|
| 66 |
+
num_iterations_total = 4000 # Total number of simulation steps
|
| 67 |
+
initial_learning_rate = 0.01 # Initial step size for moving centers
|
| 68 |
+
k_rep = 0.08 # Strength of circle-circle repulsion
|
| 69 |
+
k_wall = 0.09 # Strength of wall repulsion
|
| 70 |
+
|
| 71 |
+
# The learning rate decays over time, simulating an annealing process.
|
| 72 |
+
# This allows for larger movements initially and finer adjustments later.
|
| 73 |
+
current_learning_rate = initial_learning_rate
|
| 74 |
+
|
| 75 |
+
for iteration in range(num_iterations_total):
|
| 76 |
+
# 1. Compute radii for current centers.
|
| 77 |
+
# This function aims to maximize radii such that no circles overlap
|
| 78 |
+
# and all are within bounds for the given center positions.
|
| 79 |
+
radii = compute_max_radii_iterative(centers)
|
| 80 |
+
|
| 81 |
+
# 2. Calculate forces on centers.
|
| 82 |
+
# These forces push circles apart if they overlap or are too close to walls.
|
| 83 |
+
forces = np.zeros((n, 2))
|
| 84 |
+
|
| 85 |
+
for i in range(n):
|
| 86 |
+
# Wall repulsion: push circles away from boundaries if their radius extends beyond.
|
| 87 |
+
x, y = centers[i]
|
| 88 |
+
r = radii[i]
|
| 89 |
+
|
| 90 |
+
if x - r < 0: forces[i, 0] += k_wall * (r - x)
|
| 91 |
+
if x + r > 1: forces[i, 0] -= k_wall * ((x + r) - 1)
|
| 92 |
+
if y - r < 0: forces[i, 1] += k_wall * (r - y)
|
| 93 |
+
if y + r > 1: forces[i, 1] -= k_wall * ((y + r) - 1)
|
| 94 |
+
|
| 95 |
+
# Circle-circle repulsion: push overlapping circles apart.
|
| 96 |
+
for j in range(i + 1, n):
|
| 97 |
+
vec = centers[i] - centers[j]
|
| 98 |
+
dist = np.linalg.norm(vec)
|
| 99 |
+
|
| 100 |
+
min_separation = radii[i] + radii[j]
|
| 101 |
+
if dist < min_separation + 1e-9: # Overlap detected (with small tolerance)
|
| 102 |
+
overlap_amount = min_separation - dist
|
| 103 |
+
if dist > 1e-9: # Avoid division by zero if centers are too close
|
| 104 |
+
force_magnitude = k_rep * overlap_amount / dist
|
| 105 |
+
forces[i] += force_magnitude * vec
|
| 106 |
+
forces[j] -= force_magnitude * vec
|
| 107 |
+
else: # Centers are virtually identical, push in opposite random directions
|
| 108 |
+
random_vec = np.random.uniform(-1, 1, 2)
|
| 109 |
+
random_vec /= np.linalg.norm(random_vec)
|
| 110 |
+
forces[i] += k_rep * overlap_amount * random_vec
|
| 111 |
+
forces[j] -= k_rep * overlap_amount * random_vec
|
| 112 |
+
|
| 113 |
+
# 3. Update centers based on calculated forces.
|
| 114 |
+
centers += current_learning_rate * forces
|
| 115 |
+
|
| 116 |
+
# 4. Decay learning rate for annealing.
|
| 117 |
+
current_learning_rate = initial_learning_rate * (1 - iteration / num_iterations_total)
|
| 118 |
+
current_learning_rate = max(current_learning_rate, initial_learning_rate * 0.01) # Maintain a minimum step size
|
| 119 |
+
|
| 120 |
+
# 5. Clip centers to ensure they stay within the unit square.
|
| 121 |
+
# This is important for `compute_max_radii_iterative` to correctly calculate boundary limits.
|
| 122 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 123 |
+
|
| 124 |
+
# Final computation of radii after the simulation has settled.
|
| 125 |
+
final_radii = compute_max_radii_iterative(centers)
|
| 126 |
+
|
| 127 |
+
return centers, final_radii
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def compute_max_radii_iterative(centers):
|
| 131 |
+
"""
|
| 132 |
+
Computes the maximum possible radii for given circle centers such that all
|
| 133 |
+
circles are disjoint and contained within the unit square. This is an
|
| 134 |
+
iterative relaxation method that ensures convergence by repeatedly
|
| 135 |
+
checking and resolving overlaps.
|
| 136 |
+
|
| 137 |
+
Args:
|
| 138 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates of circle centers.
|
| 139 |
+
|
| 140 |
+
Returns:
|
| 141 |
+
np.array of shape (n) with the radius of each circle.
|
| 142 |
+
"""
|
| 143 |
+
n = centers.shape[0]
|
| 144 |
+
radii = np.zeros(n)
|
| 145 |
+
|
| 146 |
+
# Initialize radii based on the maximum possible distance to the nearest boundary.
|
| 147 |
+
# This ensures circles start as large as possible given only boundary constraints.
|
| 148 |
+
for i in range(n):
|
| 149 |
+
x, y = centers[i]
|
| 150 |
+
radii[i] = min(x, 1 - x, y, 1 - y)
|
| 151 |
+
radii[i] = max(radii[i], 1e-9) # Ensure a small positive radius to start.
|
| 152 |
+
|
| 153 |
+
# Iteratively adjust radii to prevent overlaps between circles.
|
| 154 |
+
# The loop runs for a fixed number of iterations to allow changes to propagate
|
| 155 |
+
# through the system until a stable state (or near-stable) is reached.
|
| 156 |
+
max_radii_solver_iterations = 200
|
| 157 |
+
|
| 158 |
+
for _ in range(max_radii_solver_iterations):
|
| 159 |
+
changed = False
|
| 160 |
+
for i in range(n):
|
| 161 |
+
# Re-check boundary conditions as radii might have been adjusted for overlaps.
|
| 162 |
+
x, y = centers[i]
|
| 163 |
+
boundary_limit = min(x, 1 - x, y, 1 - y)
|
| 164 |
+
if radii[i] > boundary_limit + 1e-9: # If radius exceeds boundary limit
|
| 165 |
+
radii[i] = boundary_limit
|
| 166 |
+
changed = True
|
| 167 |
+
|
| 168 |
+
# Check for overlaps with all other circles.
|
| 169 |
+
for j in range(n):
|
| 170 |
+
if i == j: continue
|
| 171 |
+
|
| 172 |
+
dist = np.linalg.norm(centers[i] - centers[j])
|
| 173 |
+
|
| 174 |
+
# If circles i and j overlap (with a small tolerance)
|
| 175 |
+
if radii[i] + radii[j] > dist + 1e-9:
|
| 176 |
+
sum_current_radii = radii[i] + radii[j]
|
| 177 |
+
if sum_current_radii > 1e-9: # Avoid division by zero
|
| 178 |
+
# Scale both radii proportionally so they just touch.
|
| 179 |
+
scale = dist / sum_current_radii
|
| 180 |
+
radii[i] *= scale
|
| 181 |
+
radii[j] *= scale
|
| 182 |
+
changed = True
|
| 183 |
+
else: # Both radii are near zero and centers are close. Assign tiny non-overlapping radii.
|
| 184 |
+
radii[i] = dist / 2.0 - 1e-10
|
| 185 |
+
radii[j] = dist / 2.0 - 1e-10
|
| 186 |
+
radii[i] = max(radii[i], 1e-9)
|
| 187 |
+
radii[j] = max(radii[j], 1e-9)
|
| 188 |
+
changed = True
|
| 189 |
+
if not changed:
|
| 190 |
+
break # If no radii were adjusted in an iteration, we've converged.
|
| 191 |
+
|
| 192 |
+
# Final pass to ensure all radii are non-negative and strictly within boundary limits.
|
| 193 |
+
for i in range(n):
|
| 194 |
+
x, y = centers[i]
|
| 195 |
+
radii[i] = max(0.0, radii[i]) # Radii must be non-negative.
|
| 196 |
+
radii[i] = min(radii[i], x, 1 - x, y, 1 - y) # Final clamp to boundaries.
|
| 197 |
+
|
| 198 |
+
return radii
|
| 199 |
+
# EVOLVE-BLOCK-END
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
# This part remains fixed (not evolved)
|
| 203 |
+
def run_packing():
|
| 204 |
+
"""Run the circle packing constructor for n=26"""
|
| 205 |
+
centers, radii = construct_packing()
|
| 206 |
+
# Calculate the sum of radii
|
| 207 |
+
sum_radii = np.sum(radii)
|
| 208 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/original.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
def construct_packing():
|
| 5 |
+
"""
|
| 6 |
+
Construct an arrangement of 26 circles in a unit square
|
| 7 |
+
using a force-directed simulation to maximize the sum of their radii.
|
| 8 |
+
|
| 9 |
+
This algorithm starts with an initial circle placement and iteratively
|
| 10 |
+
adjusts their positions based on repulsive forces between overlapping
|
| 11 |
+
circles and from the square boundaries, while continuously maximizing
|
| 12 |
+
their radii for the current positions. This allows the system to
|
| 13 |
+
settle into a more optimal, tightly packed configuration.
|
| 14 |
+
|
| 15 |
+
Returns:
|
| 16 |
+
Tuple of (centers, radii)
|
| 17 |
+
centers: np.array of shape (26, 2) with (x, y) coordinates
|
| 18 |
+
radii: np.array of shape (26) with radius of each circle
|
| 19 |
+
"""
|
| 20 |
+
n = 26
|
| 21 |
+
centers = np.zeros((n, 2))
|
| 22 |
+
|
| 23 |
+
# Initial placement: a slightly perturbed grid of 5x5 circles + 1 extra
|
| 24 |
+
# This provides a reasonable starting distribution for the simulation.
|
| 25 |
+
grid_side = 5
|
| 26 |
+
spacing = 1.0 / (grid_side + 1) # Distribute centers from 1/6 to 5/6
|
| 27 |
+
|
| 28 |
+
count = 0
|
| 29 |
+
for i in range(grid_side):
|
| 30 |
+
for j in range(grid_side):
|
| 31 |
+
if count < n: # Place up to 25 circles in the 5x5 grid
|
| 32 |
+
centers[count] = [(i + 1) * spacing, (j + 1) * spacing]
|
| 33 |
+
count += 1
|
| 34 |
+
|
| 35 |
+
# Place the 26th circle. For 26, one remains after 5x5 grid (25 circles).
|
| 36 |
+
if count < n:
|
| 37 |
+
centers[count] = [0.5, 0.5] # Place it at the center of the square
|
| 38 |
+
count += 1
|
| 39 |
+
|
| 40 |
+
# Add small random perturbation to initial positions to break symmetry.
|
| 41 |
+
# This helps the optimization escape local minima and explore more diverse configurations.
|
| 42 |
+
centers += np.random.uniform(-0.02, 0.02, centers.shape)
|
| 43 |
+
|
| 44 |
+
# Ensure initial centers are within the [0,1] bounds
|
| 45 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 46 |
+
|
| 47 |
+
# --- Force-directed simulation parameters ---
|
| 48 |
+
num_iterations_total = 2500 # Total number of simulation steps
|
| 49 |
+
initial_learning_rate = 0.01 # Initial step size for moving centers
|
| 50 |
+
k_rep = 0.05 # Strength of circle-circle repulsion
|
| 51 |
+
k_wall = 0.1 # Strength of wall repulsion
|
| 52 |
+
|
| 53 |
+
# The learning rate decays over time, simulating an annealing process.
|
| 54 |
+
# This allows for larger movements initially and finer adjustments later.
|
| 55 |
+
current_learning_rate = initial_learning_rate
|
| 56 |
+
|
| 57 |
+
for iteration in range(num_iterations_total):
|
| 58 |
+
# 1. Compute radii for current centers.
|
| 59 |
+
# This function aims to maximize radii such that no circles overlap
|
| 60 |
+
# and all are within bounds for the given center positions.
|
| 61 |
+
radii = compute_max_radii_iterative(centers)
|
| 62 |
+
|
| 63 |
+
# 2. Calculate forces on centers.
|
| 64 |
+
# These forces push circles apart if they overlap or are too close to walls.
|
| 65 |
+
forces = np.zeros((n, 2))
|
| 66 |
+
|
| 67 |
+
for i in range(n):
|
| 68 |
+
# Wall repulsion: push circles away from boundaries if their radius extends beyond.
|
| 69 |
+
x, y = centers[i]
|
| 70 |
+
r = radii[i]
|
| 71 |
+
|
| 72 |
+
if x - r < 0: forces[i, 0] += k_wall * (r - x)
|
| 73 |
+
if x + r > 1: forces[i, 0] -= k_wall * ((x + r) - 1)
|
| 74 |
+
if y - r < 0: forces[i, 1] += k_wall * (r - y)
|
| 75 |
+
if y + r > 1: forces[i, 1] -= k_wall * ((y + r) - 1)
|
| 76 |
+
|
| 77 |
+
# Circle-circle repulsion: push overlapping circles apart.
|
| 78 |
+
for j in range(i + 1, n):
|
| 79 |
+
vec = centers[i] - centers[j]
|
| 80 |
+
dist = np.linalg.norm(vec)
|
| 81 |
+
|
| 82 |
+
min_separation = radii[i] + radii[j]
|
| 83 |
+
if dist < min_separation + 1e-9: # Overlap detected (with small tolerance)
|
| 84 |
+
overlap_amount = min_separation - dist
|
| 85 |
+
if dist > 1e-9: # Avoid division by zero if centers are too close
|
| 86 |
+
force_magnitude = k_rep * overlap_amount / dist
|
| 87 |
+
forces[i] += force_magnitude * vec
|
| 88 |
+
forces[j] -= force_magnitude * vec
|
| 89 |
+
else: # Centers are virtually identical, push in opposite random directions
|
| 90 |
+
random_vec = np.random.uniform(-1, 1, 2)
|
| 91 |
+
random_vec /= np.linalg.norm(random_vec)
|
| 92 |
+
forces[i] += k_rep * overlap_amount * random_vec
|
| 93 |
+
forces[j] -= k_rep * overlap_amount * random_vec
|
| 94 |
+
|
| 95 |
+
# 3. Update centers based on calculated forces.
|
| 96 |
+
centers += current_learning_rate * forces
|
| 97 |
+
|
| 98 |
+
# 4. Decay learning rate for annealing.
|
| 99 |
+
current_learning_rate = initial_learning_rate * (1 - iteration / num_iterations_total)
|
| 100 |
+
current_learning_rate = max(current_learning_rate, initial_learning_rate * 0.01) # Maintain a minimum step size
|
| 101 |
+
|
| 102 |
+
# 5. Clip centers to ensure they stay within the unit square.
|
| 103 |
+
# This is important for `compute_max_radii_iterative` to correctly calculate boundary limits.
|
| 104 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 105 |
+
|
| 106 |
+
# Final computation of radii after the simulation has settled.
|
| 107 |
+
final_radii = compute_max_radii_iterative(centers)
|
| 108 |
+
|
| 109 |
+
return centers, final_radii
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def compute_max_radii_iterative(centers):
|
| 113 |
+
"""
|
| 114 |
+
Computes the maximum possible radii for given circle centers such that all
|
| 115 |
+
circles are disjoint and contained within the unit square. This is an
|
| 116 |
+
iterative relaxation method that ensures convergence by repeatedly
|
| 117 |
+
checking and resolving overlaps.
|
| 118 |
+
|
| 119 |
+
Args:
|
| 120 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates of circle centers.
|
| 121 |
+
|
| 122 |
+
Returns:
|
| 123 |
+
np.array of shape (n) with the radius of each circle.
|
| 124 |
+
"""
|
| 125 |
+
n = centers.shape[0]
|
| 126 |
+
radii = np.zeros(n)
|
| 127 |
+
|
| 128 |
+
# Initialize radii based on the maximum possible distance to the nearest boundary.
|
| 129 |
+
# This ensures circles start as large as possible given only boundary constraints.
|
| 130 |
+
for i in range(n):
|
| 131 |
+
x, y = centers[i]
|
| 132 |
+
radii[i] = min(x, 1 - x, y, 1 - y)
|
| 133 |
+
radii[i] = max(radii[i], 1e-9) # Ensure a small positive radius to start.
|
| 134 |
+
|
| 135 |
+
# Iteratively adjust radii to prevent overlaps between circles.
|
| 136 |
+
# The loop runs for a fixed number of iterations to allow changes to propagate
|
| 137 |
+
# through the system until a stable state (or near-stable) is reached.
|
| 138 |
+
max_radii_solver_iterations = 200
|
| 139 |
+
|
| 140 |
+
for _ in range(max_radii_solver_iterations):
|
| 141 |
+
changed = False
|
| 142 |
+
for i in range(n):
|
| 143 |
+
# Re-check boundary conditions as radii might have been adjusted for overlaps.
|
| 144 |
+
x, y = centers[i]
|
| 145 |
+
boundary_limit = min(x, 1 - x, y, 1 - y)
|
| 146 |
+
if radii[i] > boundary_limit + 1e-9: # If radius exceeds boundary limit
|
| 147 |
+
radii[i] = boundary_limit
|
| 148 |
+
changed = True
|
| 149 |
+
|
| 150 |
+
# Check for overlaps with all other circles.
|
| 151 |
+
for j in range(n):
|
| 152 |
+
if i == j: continue
|
| 153 |
+
|
| 154 |
+
dist = np.linalg.norm(centers[i] - centers[j])
|
| 155 |
+
|
| 156 |
+
# If circles i and j overlap (with a small tolerance)
|
| 157 |
+
if radii[i] + radii[j] > dist + 1e-9:
|
| 158 |
+
sum_current_radii = radii[i] + radii[j]
|
| 159 |
+
if sum_current_radii > 1e-9: # Avoid division by zero
|
| 160 |
+
# Scale both radii proportionally so they just touch.
|
| 161 |
+
scale = dist / sum_current_radii
|
| 162 |
+
radii[i] *= scale
|
| 163 |
+
radii[j] *= scale
|
| 164 |
+
changed = True
|
| 165 |
+
else: # Both radii are near zero and centers are close. Assign tiny non-overlapping radii.
|
| 166 |
+
radii[i] = dist / 2.0 - 1e-10
|
| 167 |
+
radii[j] = dist / 2.0 - 1e-10
|
| 168 |
+
radii[i] = max(radii[i], 1e-9)
|
| 169 |
+
radii[j] = max(radii[j], 1e-9)
|
| 170 |
+
changed = True
|
| 171 |
+
if not changed:
|
| 172 |
+
break # If no radii were adjusted in an iteration, we've converged.
|
| 173 |
+
|
| 174 |
+
# Final pass to ensure all radii are non-negative and strictly within boundary limits.
|
| 175 |
+
for i in range(n):
|
| 176 |
+
x, y = centers[i]
|
| 177 |
+
radii[i] = max(0.0, radii[i]) # Radii must be non-negative.
|
| 178 |
+
radii[i] = min(radii[i], x, 1 - x, y, 1 - y) # Final clamp to boundaries.
|
| 179 |
+
|
| 180 |
+
return 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_gen50_period5_20260206_004532/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_gen50_period5_20260206_004532/gen_10/results/metrics.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 1.2003765020010746,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 1.2003765020010746,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.2634, 0.2541)\n centers[1] = (0.7530, 0.7644)\n centers[2] = (0.2066, 0.3846)\n centers[3] = (0.2084, 0.6020)\n centers[4] = (0.3828, 0.2086)\n centers[5] = (0.3937, 0.7916)\n centers[6] = (0.5828, 0.1939)\n centers[7] = (0.6174, 0.8101)\n centers[8] = (0.7896, 0.4112)\n centers[9] = (0.7923, 0.6152)\n centers[10] = (0.0832, 0.2998)\n centers[11] = (0.0801, 0.6848)\n centers[12] = (0.3130, 0.1137)\n centers[13] = (0.3154, 0.9050)\n centers[14] = (0.6844, 0.0892)\n centers[15] = (0.7165, 0.9138)\n centers[16] = (0.9169, 0.3105)\n centers[17] = (0.8840, 0.7005)\n centers[18] = (0.3092, 0.4391)\n centers[19] = (0.2859, 0.5464)\n centers[20] = (0.4334, 0.3155)\n centers[21] = (0.4454, 0.6854)\n centers[22] = (0.5382, 0.2990)\n centers[23] = (0.5397, 0.7083)\n centers[24] = (0.7061, 0.4448)\n centers[25] = (0.7102, 0.5575)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 1.2003765020010746
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 164.98483295273036,
|
| 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 |
+
"radii_std_dev": 0.021580302421288444,
|
| 23 |
+
"radii_min": 0.009055836915091103,
|
| 24 |
+
"radii_max": 0.08320753300589721,
|
| 25 |
+
"radii_median": 0.04853178261846271,
|
| 26 |
+
"total_area_covered": 0.2121449052198526,
|
| 27 |
+
"avg_boundary_proximity": 0.15650578154419897
|
| 28 |
+
},
|
| 29 |
+
"auxiliary_descriptions": {
|
| 30 |
+
"radii_std_dev": "Standard deviation of the radii of all 26 circles. A higher value indicates greater diversity in circle sizes, which might be beneficial for fitting into complex spaces.",
|
| 31 |
+
"radii_min": "The minimum radius among all circles. A very small value could indicate a strategy to fill tiny gaps, while a larger value suggests a preference for more uniform, larger circles.",
|
| 32 |
+
"radii_max": "The maximum radius among all circles. A large max radius implies one or more dominant circles, while a smaller max radius indicates more evenly sized circles.",
|
| 33 |
+
"total_area_covered": "The sum of the areas of all 26 circles. This metric provides a direct measure of the total space occupied within the unit square. Higher values indicate more efficient use of space. Since the primary score is `sum(radii)`, `total_area_covered` (proportional to `sum(radii^2)`) offers a slightly different perspective on packing density.",
|
| 34 |
+
"min_boundary_distance": "The minimum distance from the edge of any circle to any of the four boundaries of the unit square. A smaller value indicates that circles are packed closer to the container's edges, suggesting more efficient use of the available packing area and potentially denser solutions.",
|
| 35 |
+
"avg_boundary_distance": "The average minimum distance from the edge of each circle to its closest boundary. This provides a smoother measure of how well, on average, the circles are utilizing the space near the boundaries.",
|
| 36 |
+
"radii_median": "The median radius among all circles. Provides a measure of the central tendency of circle sizes, less sensitive to outliers than the mean.",
|
| 37 |
+
"avg_boundary_proximity": "The average of the minimum distances from each circle's edge to any of the four boundaries of the unit square. Lower values indicate that circles are placed closer to the edges, suggesting better utilization of the square's perimeter and potentially more efficient packing."
|
| 38 |
+
},
|
| 39 |
+
"timestamp": 1770340060.643199,
|
| 40 |
+
"generation": 10
|
| 41 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_10/search_replace.txt
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
I will propose two edits. The first, and most important, is to change the initial configuration of circles from a grid-like pattern to a more complex symmetric one that is less likely to be a local minimum. The second edit adjusts the simulation parameters to better suit this new initial configuration.
|
| 2 |
+
|
| 3 |
+
<NAME>
|
| 4 |
+
symmetric_initial_placement
|
| 5 |
+
</NAME>
|
| 6 |
+
<DESCRIPTION>
|
| 7 |
+
The current initial placement is a 5x5 grid with an extra circle in the middle. For N=25, a 5x5 grid is optimal, making this configuration a very deep local minimum that the simulation struggles to escape for N=26. The result is a suboptimal packing that retains a strong grid-like structure, as seen in the final coordinates.
|
| 8 |
+
|
| 9 |
+
This edit replaces the grid initialization with a 4-fold symmetric (D4) pattern. This is constructed by placing 3 seed points in the fundamental domain (the wedge 0.5 <= y <= x <= 1), generating 24 points via symmetry operations, and adding 2 more points on the main diagonal.
|
| 10 |
+
|
| 11 |
+
This symmetric, non-grid starting configuration encourages the optimizer to explore more complex and potentially much better arrangements, as it's not starting in a deep local minimum. The subsequent random perturbation will break the perfect symmetry, allowing the system to find an optimal (even if asymmetric) solution.
|
| 12 |
+
</DESCRIPTION>
|
| 13 |
+
<DIFF>
|
| 14 |
+
<<<<<<< SEARCH
|
| 15 |
+
# Initial placement: a slightly perturbed grid of 5x5 circles + 1 extra
|
| 16 |
+
# This provides a reasonable starting distribution for the simulation.
|
| 17 |
+
grid_side = 5
|
| 18 |
+
spacing = 1.0 / (grid_side + 1) # Distribute centers from 1/6 to 5/6
|
| 19 |
+
|
| 20 |
+
count = 0
|
| 21 |
+
for i in range(grid_side):
|
| 22 |
+
for j in range(grid_side):
|
| 23 |
+
if count < n: # Place up to 25 circles in the 5x5 grid
|
| 24 |
+
centers[count] = [(i + 1) * spacing, (j + 1) * spacing]
|
| 25 |
+
count += 1
|
| 26 |
+
|
| 27 |
+
# Place the 26th circle. For 26, one remains after 5x5 grid (25 circles).
|
| 28 |
+
if count < n:
|
| 29 |
+
centers[count] = [0.5, 0.5] # Place it at the center of the square
|
| 30 |
+
count += 1
|
| 31 |
+
=======
|
| 32 |
+
# Initial placement: A 4-fold symmetric pattern (D4 symmetry).
|
| 33 |
+
# For N=26, we use 3 seed points generating 8 points each (24),
|
| 34 |
+
# plus 2 points on the main diagonal y=x.
|
| 35 |
+
# This avoids the grid-like local minimum and explores more complex structures.
|
| 36 |
+
count = 0
|
| 37 |
+
|
| 38 |
+
# 1. Two points on the main diagonal
|
| 39 |
+
diag_dist = 0.25
|
| 40 |
+
centers[count] = [0.5 - diag_dist, 0.5 - diag_dist]; count += 1
|
| 41 |
+
centers[count] = [0.5 + diag_dist, 0.5 + diag_dist]; count += 1
|
| 42 |
+
|
| 43 |
+
# 2. Three seed points in the fundamental domain wedge (0.5 <= y <= x <= 1)
|
| 44 |
+
seed_points = np.array([
|
| 45 |
+
[0.8, 0.6],
|
| 46 |
+
[0.9, 0.7],
|
| 47 |
+
[0.7, 0.55]
|
| 48 |
+
])
|
| 49 |
+
|
| 50 |
+
for p in seed_points:
|
| 51 |
+
x, y = p[0], p[1]
|
| 52 |
+
# Generate up to 8 points by D4 symmetry operations
|
| 53 |
+
base_points = {(x, y), (y, x)}
|
| 54 |
+
all_points = set()
|
| 55 |
+
for pt in base_points:
|
| 56 |
+
px, py = pt
|
| 57 |
+
all_points.add((px, py))
|
| 58 |
+
all_points.add((1 - px, py))
|
| 59 |
+
all_points.add((px, 1 - py))
|
| 60 |
+
all_points.add((1 - px, 1 - py))
|
| 61 |
+
|
| 62 |
+
for point in sorted(list(all_points)): # sorted for determinism
|
| 63 |
+
if count < n:
|
| 64 |
+
centers[count] = point
|
| 65 |
+
count += 1
|
| 66 |
+
>>>>>>> REPLACE
|
| 67 |
+
</DIFF>
|
| 68 |
+
<NAME>
|
| 69 |
+
tune_simulation_parameters
|
| 70 |
+
</NAME>
|
| 71 |
+
<DESCRIPTION>
|
| 72 |
+
To complement the new, more complex initial configuration, the simulation parameters are adjusted.
|
| 73 |
+
|
| 74 |
+
1. `num_iterations_total` is increased from 2500 to 4000. The symmetric starting point is further from a final packed state than a simple grid, so more iterations are needed for the system to settle into a good configuration.
|
| 75 |
+
2. The force constants are rebalanced. `k_rep` is increased from 0.05 to 0.08, and `k_wall` is slightly decreased from 0.1 to 0.09. The original ratio of `k_wall/k_rep = 2` may have caused circles to cluster away from the walls excessively. Making the repulsion and wall forces more equal in strength encourages circles to better utilize the entire space, including areas near the edges and corners, which is critical for optimal packing.
|
| 76 |
+
</DESCRIPTION>
|
| 77 |
+
<DIFF>
|
| 78 |
+
<<<<<<< SEARCH
|
| 79 |
+
# --- Force-directed simulation parameters ---
|
| 80 |
+
num_iterations_total = 2500 # Total number of simulation steps
|
| 81 |
+
initial_learning_rate = 0.01 # Initial step size for moving centers
|
| 82 |
+
k_rep = 0.05 # Strength of circle-circle repulsion
|
| 83 |
+
k_wall = 0.1 # Strength of wall repulsion
|
| 84 |
+
=======
|
| 85 |
+
# --- Force-directed simulation parameters ---
|
| 86 |
+
num_iterations_total = 4000 # Total number of simulation steps
|
| 87 |
+
initial_learning_rate = 0.01 # Initial step size for moving centers
|
| 88 |
+
k_rep = 0.08 # Strength of circle-circle repulsion
|
| 89 |
+
k_wall = 0.09 # Strength of wall repulsion
|
| 90 |
+
>>>>>>> REPLACE
|
| 91 |
+
</DIFF>
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (6.32 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/edit.diff
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--- a/original.py
|
| 2 |
+
+++ b/original.py
|
| 3 |
+
@@ -1,190 +1,186 @@
|
| 4 |
+
# EVOLVE-BLOCK-START
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
def construct_packing():
|
| 8 |
+
"""
|
| 9 |
+
Construct an arrangement of 26 circles in a unit square
|
| 10 |
+
using a force-directed simulation to maximize the sum of their radii.
|
| 11 |
+
|
| 12 |
+
- This algorithm starts with an initial circle placement and iteratively
|
| 13 |
+
- adjusts their positions based on repulsive forces between overlapping
|
| 14 |
+
- circles and from the square boundaries, while continuously maximizing
|
| 15 |
+
- their radii for the current positions. This allows the system to
|
| 16 |
+
- settle into a more optimal, tightly packed configuration.
|
| 17 |
+
+ This algorithm starts with an initial concentric ring placement and iteratively
|
| 18 |
+
+ adjusts their positions based on repulsive forces. This hybrid approach
|
| 19 |
+
+ leverages a structured start to guide the optimization to a better result.
|
| 20 |
+
|
| 21 |
+
Returns:
|
| 22 |
+
Tuple of (centers, radii)
|
| 23 |
+
centers: np.array of shape (26, 2) with (x, y) coordinates
|
| 24 |
+
radii: np.array of shape (26) with radius of each circle
|
| 25 |
+
"""
|
| 26 |
+
n = 26
|
| 27 |
+
centers = np.zeros((n, 2))
|
| 28 |
+
|
| 29 |
+
- # Initial placement: a slightly perturbed grid of 5x5 circles + 1 extra
|
| 30 |
+
- # This provides a reasonable starting distribution for the simulation.
|
| 31 |
+
- grid_side = 5
|
| 32 |
+
- spacing = 1.0 / (grid_side + 1) # Distribute centers from 1/6 to 5/6
|
| 33 |
+
+ # Initial placement: Concentric rings (1 + 8 + 17).
|
| 34 |
+
+ # This provides a structured, non-grid starting point that can lead
|
| 35 |
+
+ # to better packing configurations. The radii are chosen to keep all
|
| 36 |
+
+ # circles well within the unit square initially.
|
| 37 |
+
|
| 38 |
+
- count = 0
|
| 39 |
+
- for i in range(grid_side):
|
| 40 |
+
- for j in range(grid_side):
|
| 41 |
+
- if count < n: # Place up to 25 circles in the 5x5 grid
|
| 42 |
+
- centers[count] = [(i + 1) * spacing, (j + 1) * spacing]
|
| 43 |
+
- count += 1
|
| 44 |
+
-
|
| 45 |
+
- # Place the 26th circle. For 26, one remains after 5x5 grid (25 circles).
|
| 46 |
+
- if count < n:
|
| 47 |
+
- centers[count] = [0.5, 0.5] # Place it at the center of the square
|
| 48 |
+
- count += 1
|
| 49 |
+
+ # Central circle
|
| 50 |
+
+ centers[0] = [0.5, 0.5]
|
| 51 |
+
+
|
| 52 |
+
+ # Inner ring of 8 circles
|
| 53 |
+
+ r_inner = 0.22 # Radius of the inner ring circle centers
|
| 54 |
+
+ num_inner = 8
|
| 55 |
+
+ for i in range(num_inner):
|
| 56 |
+
+ angle = 2 * np.pi * i / num_inner
|
| 57 |
+
+ centers[i + 1] = [0.5 + r_inner * np.cos(angle), 0.5 + r_inner * np.sin(angle)]
|
| 58 |
+
+
|
| 59 |
+
+ # Outer ring of 17 circles
|
| 60 |
+
+ r_outer = 0.42 # Radius of the outer ring circle centers
|
| 61 |
+
+ num_outer = 17
|
| 62 |
+
+ for i in range(num_outer):
|
| 63 |
+
+ angle = 2 * np.pi * i / num_outer
|
| 64 |
+
+ # The offset is 1 (center) + num_inner
|
| 65 |
+
+ centers[i + 1 + num_inner] = [0.5 + r_outer * np.cos(angle), 0.5 + r_outer * np.sin(angle)]
|
| 66 |
+
+
|
| 67 |
+
|
| 68 |
+
# Add small random perturbation to initial positions to break symmetry.
|
| 69 |
+
# This helps the optimization escape local minima and explore more diverse configurations.
|
| 70 |
+
centers += np.random.uniform(-0.02, 0.02, centers.shape)
|
| 71 |
+
|
| 72 |
+
# Ensure initial centers are within the [0,1] bounds
|
| 73 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 74 |
+
|
| 75 |
+
# --- Force-directed simulation parameters ---
|
| 76 |
+
num_iterations_total = 2500 # Total number of simulation steps
|
| 77 |
+
initial_learning_rate = 0.01 # Initial step size for moving centers
|
| 78 |
+
k_rep = 0.05 # Strength of circle-circle repulsion
|
| 79 |
+
k_wall = 0.1 # Strength of wall repulsion
|
| 80 |
+
|
| 81 |
+
# The learning rate decays over time, simulating an annealing process.
|
| 82 |
+
# This allows for larger movements initially and finer adjustments later.
|
| 83 |
+
current_learning_rate = initial_learning_rate
|
| 84 |
+
|
| 85 |
+
for iteration in range(num_iterations_total):
|
| 86 |
+
# 1. Compute radii for current centers.
|
| 87 |
+
- # This function aims to maximize radii such that no circles overlap
|
| 88 |
+
- # and all are within bounds for the given center positions.
|
| 89 |
+
radii = compute_max_radii_iterative(centers)
|
| 90 |
+
|
| 91 |
+
# 2. Calculate forces on centers.
|
| 92 |
+
- # These forces push circles apart if they overlap or are too close to walls.
|
| 93 |
+
forces = np.zeros((n, 2))
|
| 94 |
+
|
| 95 |
+
for i in range(n):
|
| 96 |
+
- # Wall repulsion: push circles away from boundaries if their radius extends beyond.
|
| 97 |
+
+ # Wall repulsion: push circles away from boundaries.
|
| 98 |
+
x, y = centers[i]
|
| 99 |
+
r = radii[i]
|
| 100 |
+
|
| 101 |
+
if x - r < 0: forces[i, 0] += k_wall * (r - x)
|
| 102 |
+
if x + r > 1: forces[i, 0] -= k_wall * ((x + r) - 1)
|
| 103 |
+
if y - r < 0: forces[i, 1] += k_wall * (r - y)
|
| 104 |
+
if y + r > 1: forces[i, 1] -= k_wall * ((y + r) - 1)
|
| 105 |
+
|
| 106 |
+
# Circle-circle repulsion: push overlapping circles apart.
|
| 107 |
+
for j in range(i + 1, n):
|
| 108 |
+
vec = centers[i] - centers[j]
|
| 109 |
+
dist = np.linalg.norm(vec)
|
| 110 |
+
|
| 111 |
+
min_separation = radii[i] + radii[j]
|
| 112 |
+
- if dist < min_separation + 1e-9: # Overlap detected (with small tolerance)
|
| 113 |
+
+ if dist < min_separation + 1e-9: # Overlap detected
|
| 114 |
+
overlap_amount = min_separation - dist
|
| 115 |
+
- if dist > 1e-9: # Avoid division by zero if centers are too close
|
| 116 |
+
+ if dist > 1e-9:
|
| 117 |
+
force_magnitude = k_rep * overlap_amount / dist
|
| 118 |
+
forces[i] += force_magnitude * vec
|
| 119 |
+
forces[j] -= force_magnitude * vec
|
| 120 |
+
- else: # Centers are virtually identical, push in opposite random directions
|
| 121 |
+
+ else: # Centers are virtually identical
|
| 122 |
+
random_vec = np.random.uniform(-1, 1, 2)
|
| 123 |
+
random_vec /= np.linalg.norm(random_vec)
|
| 124 |
+
forces[i] += k_rep * overlap_amount * random_vec
|
| 125 |
+
forces[j] -= k_rep * overlap_amount * random_vec
|
| 126 |
+
|
| 127 |
+
# 3. Update centers based on calculated forces.
|
| 128 |
+
centers += current_learning_rate * forces
|
| 129 |
+
|
| 130 |
+
# 4. Decay learning rate for annealing.
|
| 131 |
+
current_learning_rate = initial_learning_rate * (1 - iteration / num_iterations_total)
|
| 132 |
+
- current_learning_rate = max(current_learning_rate, initial_learning_rate * 0.01) # Maintain a minimum step size
|
| 133 |
+
+ current_learning_rate = max(current_learning_rate, initial_learning_rate * 0.01)
|
| 134 |
+
|
| 135 |
+
# 5. Clip centers to ensure they stay within the unit square.
|
| 136 |
+
- # This is important for `compute_max_radii_iterative` to correctly calculate boundary limits.
|
| 137 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 138 |
+
|
| 139 |
+
# Final computation of radii after the simulation has settled.
|
| 140 |
+
final_radii = compute_max_radii_iterative(centers)
|
| 141 |
+
|
| 142 |
+
return centers, final_radii
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def compute_max_radii_iterative(centers):
|
| 146 |
+
"""
|
| 147 |
+
Computes the maximum possible radii for given circle centers such that all
|
| 148 |
+
circles are disjoint and contained within the unit square. This is an
|
| 149 |
+
- iterative relaxation method that ensures convergence by repeatedly
|
| 150 |
+
- checking and resolving overlaps.
|
| 151 |
+
+ iterative relaxation method.
|
| 152 |
+
|
| 153 |
+
Args:
|
| 154 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates of circle centers.
|
| 155 |
+
|
| 156 |
+
Returns:
|
| 157 |
+
np.array of shape (n) with the radius of each circle.
|
| 158 |
+
"""
|
| 159 |
+
n = centers.shape[0]
|
| 160 |
+
radii = np.zeros(n)
|
| 161 |
+
|
| 162 |
+
# Initialize radii based on the maximum possible distance to the nearest boundary.
|
| 163 |
+
- # This ensures circles start as large as possible given only boundary constraints.
|
| 164 |
+
for i in range(n):
|
| 165 |
+
x, y = centers[i]
|
| 166 |
+
radii[i] = min(x, 1 - x, y, 1 - y)
|
| 167 |
+
- radii[i] = max(radii[i], 1e-9) # Ensure a small positive radius to start.
|
| 168 |
+
+ radii[i] = max(radii[i], 1e-9)
|
| 169 |
+
|
| 170 |
+
# Iteratively adjust radii to prevent overlaps between circles.
|
| 171 |
+
- # The loop runs for a fixed number of iterations to allow changes to propagate
|
| 172 |
+
- # through the system until a stable state (or near-stable) is reached.
|
| 173 |
+
max_radii_solver_iterations = 200
|
| 174 |
+
|
| 175 |
+
for _ in range(max_radii_solver_iterations):
|
| 176 |
+
changed = False
|
| 177 |
+
for i in range(n):
|
| 178 |
+
- # Re-check boundary conditions as radii might have been adjusted for overlaps.
|
| 179 |
+
+ # Re-check boundary conditions.
|
| 180 |
+
x, y = centers[i]
|
| 181 |
+
boundary_limit = min(x, 1 - x, y, 1 - y)
|
| 182 |
+
- if radii[i] > boundary_limit + 1e-9: # If radius exceeds boundary limit
|
| 183 |
+
+ if radii[i] > boundary_limit + 1e-9:
|
| 184 |
+
radii[i] = boundary_limit
|
| 185 |
+
changed = True
|
| 186 |
+
|
| 187 |
+
# Check for overlaps with all other circles.
|
| 188 |
+
for j in range(n):
|
| 189 |
+
if i == j: continue
|
| 190 |
+
|
| 191 |
+
dist = np.linalg.norm(centers[i] - centers[j])
|
| 192 |
+
|
| 193 |
+
- # If circles i and j overlap (with a small tolerance)
|
| 194 |
+
if radii[i] + radii[j] > dist + 1e-9:
|
| 195 |
+
sum_current_radii = radii[i] + radii[j]
|
| 196 |
+
- if sum_current_radii > 1e-9: # Avoid division by zero
|
| 197 |
+
+ if sum_current_radii > 1e-9:
|
| 198 |
+
# Scale both radii proportionally so they just touch.
|
| 199 |
+
scale = dist / sum_current_radii
|
| 200 |
+
radii[i] *= scale
|
| 201 |
+
radii[j] *= scale
|
| 202 |
+
changed = True
|
| 203 |
+
- else: # Both radii are near zero and centers are close. Assign tiny non-overlapping radii.
|
| 204 |
+
+ else:
|
| 205 |
+
radii[i] = dist / 2.0 - 1e-10
|
| 206 |
+
radii[j] = dist / 2.0 - 1e-10
|
| 207 |
+
radii[i] = max(radii[i], 1e-9)
|
| 208 |
+
radii[j] = max(radii[j], 1e-9)
|
| 209 |
+
changed = True
|
| 210 |
+
if not changed:
|
| 211 |
+
- break # If no radii were adjusted in an iteration, we've converged.
|
| 212 |
+
+ break
|
| 213 |
+
|
| 214 |
+
# Final pass to ensure all radii are non-negative and strictly within boundary limits.
|
| 215 |
+
for i in range(n):
|
| 216 |
+
x, y = centers[i]
|
| 217 |
+
- radii[i] = max(0.0, radii[i]) # Radii must be non-negative.
|
| 218 |
+
- radii[i] = min(radii[i], x, 1 - x, y, 1 - y) # Final clamp to boundaries.
|
| 219 |
+
+ radii[i] = max(0.0, radii[i])
|
| 220 |
+
+ radii[i] = min(radii[i], x, 1 - x, y, 1 - y)
|
| 221 |
+
|
| 222 |
+
return radii
|
| 223 |
+
# EVOLVE-BLOCK-END
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
# This part remains fixed (not evolved)
|
| 227 |
+
def run_packing():
|
| 228 |
+
"""Run the circle packing constructor for n=26"""
|
| 229 |
+
centers, radii = construct_packing()
|
| 230 |
+
# Calculate the sum of radii
|
| 231 |
+
sum_radii = np.sum(radii)
|
| 232 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/main.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
def construct_packing():
|
| 5 |
+
"""
|
| 6 |
+
Construct an arrangement of 26 circles in a unit square
|
| 7 |
+
using a force-directed simulation to maximize the sum of their radii.
|
| 8 |
+
|
| 9 |
+
This algorithm starts with an initial concentric ring placement and iteratively
|
| 10 |
+
adjusts their positions based on repulsive forces. This hybrid approach
|
| 11 |
+
leverages a structured start to guide the optimization to a better result.
|
| 12 |
+
|
| 13 |
+
Returns:
|
| 14 |
+
Tuple of (centers, radii)
|
| 15 |
+
centers: np.array of shape (26, 2) with (x, y) coordinates
|
| 16 |
+
radii: np.array of shape (26) with radius of each circle
|
| 17 |
+
"""
|
| 18 |
+
n = 26
|
| 19 |
+
centers = np.zeros((n, 2))
|
| 20 |
+
|
| 21 |
+
# Initial placement: Concentric rings (1 + 8 + 17).
|
| 22 |
+
# This provides a structured, non-grid starting point that can lead
|
| 23 |
+
# to better packing configurations. The radii are chosen to keep all
|
| 24 |
+
# circles well within the unit square initially.
|
| 25 |
+
|
| 26 |
+
# Central circle
|
| 27 |
+
centers[0] = [0.5, 0.5]
|
| 28 |
+
|
| 29 |
+
# Inner ring of 8 circles
|
| 30 |
+
r_inner = 0.22 # Radius of the inner ring circle centers
|
| 31 |
+
num_inner = 8
|
| 32 |
+
for i in range(num_inner):
|
| 33 |
+
angle = 2 * np.pi * i / num_inner
|
| 34 |
+
centers[i + 1] = [0.5 + r_inner * np.cos(angle), 0.5 + r_inner * np.sin(angle)]
|
| 35 |
+
|
| 36 |
+
# Outer ring of 17 circles
|
| 37 |
+
r_outer = 0.42 # Radius of the outer ring circle centers
|
| 38 |
+
num_outer = 17
|
| 39 |
+
for i in range(num_outer):
|
| 40 |
+
angle = 2 * np.pi * i / num_outer
|
| 41 |
+
# The offset is 1 (center) + num_inner
|
| 42 |
+
centers[i + 1 + num_inner] = [0.5 + r_outer * np.cos(angle), 0.5 + r_outer * np.sin(angle)]
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
# Add small random perturbation to initial positions to break symmetry.
|
| 46 |
+
# This helps the optimization escape local minima and explore more diverse configurations.
|
| 47 |
+
centers += np.random.uniform(-0.02, 0.02, centers.shape)
|
| 48 |
+
|
| 49 |
+
# Ensure initial centers are within the [0,1] bounds
|
| 50 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 51 |
+
|
| 52 |
+
# --- Force-directed simulation parameters ---
|
| 53 |
+
num_iterations_total = 2500 # Total number of simulation steps
|
| 54 |
+
initial_learning_rate = 0.01 # Initial step size for moving centers
|
| 55 |
+
k_rep = 0.05 # Strength of circle-circle repulsion
|
| 56 |
+
k_wall = 0.1 # Strength of wall repulsion
|
| 57 |
+
|
| 58 |
+
# The learning rate decays over time, simulating an annealing process.
|
| 59 |
+
# This allows for larger movements initially and finer adjustments later.
|
| 60 |
+
current_learning_rate = initial_learning_rate
|
| 61 |
+
|
| 62 |
+
for iteration in range(num_iterations_total):
|
| 63 |
+
# 1. Compute radii for current centers.
|
| 64 |
+
radii = compute_max_radii_iterative(centers)
|
| 65 |
+
|
| 66 |
+
# 2. Calculate forces on centers.
|
| 67 |
+
forces = np.zeros((n, 2))
|
| 68 |
+
|
| 69 |
+
for i in range(n):
|
| 70 |
+
# Wall repulsion: push circles away from boundaries.
|
| 71 |
+
x, y = centers[i]
|
| 72 |
+
r = radii[i]
|
| 73 |
+
|
| 74 |
+
if x - r < 0: forces[i, 0] += k_wall * (r - x)
|
| 75 |
+
if x + r > 1: forces[i, 0] -= k_wall * ((x + r) - 1)
|
| 76 |
+
if y - r < 0: forces[i, 1] += k_wall * (r - y)
|
| 77 |
+
if y + r > 1: forces[i, 1] -= k_wall * ((y + r) - 1)
|
| 78 |
+
|
| 79 |
+
# Circle-circle repulsion: push overlapping circles apart.
|
| 80 |
+
for j in range(i + 1, n):
|
| 81 |
+
vec = centers[i] - centers[j]
|
| 82 |
+
dist = np.linalg.norm(vec)
|
| 83 |
+
|
| 84 |
+
min_separation = radii[i] + radii[j]
|
| 85 |
+
if dist < min_separation + 1e-9: # Overlap detected
|
| 86 |
+
overlap_amount = min_separation - dist
|
| 87 |
+
if dist > 1e-9:
|
| 88 |
+
force_magnitude = k_rep * overlap_amount / dist
|
| 89 |
+
forces[i] += force_magnitude * vec
|
| 90 |
+
forces[j] -= force_magnitude * vec
|
| 91 |
+
else: # Centers are virtually identical
|
| 92 |
+
random_vec = np.random.uniform(-1, 1, 2)
|
| 93 |
+
random_vec /= np.linalg.norm(random_vec)
|
| 94 |
+
forces[i] += k_rep * overlap_amount * random_vec
|
| 95 |
+
forces[j] -= k_rep * overlap_amount * random_vec
|
| 96 |
+
|
| 97 |
+
# 3. Update centers based on calculated forces.
|
| 98 |
+
centers += current_learning_rate * forces
|
| 99 |
+
|
| 100 |
+
# 4. Decay learning rate for annealing.
|
| 101 |
+
current_learning_rate = initial_learning_rate * (1 - iteration / num_iterations_total)
|
| 102 |
+
current_learning_rate = max(current_learning_rate, initial_learning_rate * 0.01)
|
| 103 |
+
|
| 104 |
+
# 5. Clip centers to ensure they stay within the unit square.
|
| 105 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 106 |
+
|
| 107 |
+
# Final computation of radii after the simulation has settled.
|
| 108 |
+
final_radii = compute_max_radii_iterative(centers)
|
| 109 |
+
|
| 110 |
+
return centers, final_radii
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def compute_max_radii_iterative(centers):
|
| 114 |
+
"""
|
| 115 |
+
Computes the maximum possible radii for given circle centers such that all
|
| 116 |
+
circles are disjoint and contained within the unit square. This is an
|
| 117 |
+
iterative relaxation method.
|
| 118 |
+
|
| 119 |
+
Args:
|
| 120 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates of circle centers.
|
| 121 |
+
|
| 122 |
+
Returns:
|
| 123 |
+
np.array of shape (n) with the radius of each circle.
|
| 124 |
+
"""
|
| 125 |
+
n = centers.shape[0]
|
| 126 |
+
radii = np.zeros(n)
|
| 127 |
+
|
| 128 |
+
# Initialize radii based on the maximum possible distance to the nearest boundary.
|
| 129 |
+
for i in range(n):
|
| 130 |
+
x, y = centers[i]
|
| 131 |
+
radii[i] = min(x, 1 - x, y, 1 - y)
|
| 132 |
+
radii[i] = max(radii[i], 1e-9)
|
| 133 |
+
|
| 134 |
+
# Iteratively adjust radii to prevent overlaps between circles.
|
| 135 |
+
max_radii_solver_iterations = 200
|
| 136 |
+
|
| 137 |
+
for _ in range(max_radii_solver_iterations):
|
| 138 |
+
changed = False
|
| 139 |
+
for i in range(n):
|
| 140 |
+
# Re-check boundary conditions.
|
| 141 |
+
x, y = centers[i]
|
| 142 |
+
boundary_limit = min(x, 1 - x, y, 1 - y)
|
| 143 |
+
if radii[i] > boundary_limit + 1e-9:
|
| 144 |
+
radii[i] = boundary_limit
|
| 145 |
+
changed = True
|
| 146 |
+
|
| 147 |
+
# Check for overlaps with all other circles.
|
| 148 |
+
for j in range(n):
|
| 149 |
+
if i == j: continue
|
| 150 |
+
|
| 151 |
+
dist = np.linalg.norm(centers[i] - centers[j])
|
| 152 |
+
|
| 153 |
+
if radii[i] + radii[j] > dist + 1e-9:
|
| 154 |
+
sum_current_radii = radii[i] + radii[j]
|
| 155 |
+
if sum_current_radii > 1e-9:
|
| 156 |
+
# Scale both radii proportionally so they just touch.
|
| 157 |
+
scale = dist / sum_current_radii
|
| 158 |
+
radii[i] *= scale
|
| 159 |
+
radii[j] *= scale
|
| 160 |
+
changed = True
|
| 161 |
+
else:
|
| 162 |
+
radii[i] = dist / 2.0 - 1e-10
|
| 163 |
+
radii[j] = dist / 2.0 - 1e-10
|
| 164 |
+
radii[i] = max(radii[i], 1e-9)
|
| 165 |
+
radii[j] = max(radii[j], 1e-9)
|
| 166 |
+
changed = True
|
| 167 |
+
if not changed:
|
| 168 |
+
break
|
| 169 |
+
|
| 170 |
+
# Final pass to ensure all radii are non-negative and strictly within boundary limits.
|
| 171 |
+
for i in range(n):
|
| 172 |
+
x, y = centers[i]
|
| 173 |
+
radii[i] = max(0.0, radii[i])
|
| 174 |
+
radii[i] = min(radii[i], x, 1 - x, y, 1 - y)
|
| 175 |
+
|
| 176 |
+
return radii
|
| 177 |
+
# EVOLVE-BLOCK-END
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
# This part remains fixed (not evolved)
|
| 181 |
+
def run_packing():
|
| 182 |
+
"""Run the circle packing constructor for n=26"""
|
| 183 |
+
centers, radii = construct_packing()
|
| 184 |
+
# Calculate the sum of radii
|
| 185 |
+
sum_radii = np.sum(radii)
|
| 186 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/original.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
def construct_packing():
|
| 5 |
+
"""
|
| 6 |
+
Construct an arrangement of 26 circles in a unit square
|
| 7 |
+
using a force-directed simulation to maximize the sum of their radii.
|
| 8 |
+
|
| 9 |
+
This algorithm starts with an initial circle placement and iteratively
|
| 10 |
+
adjusts their positions based on repulsive forces between overlapping
|
| 11 |
+
circles and from the square boundaries, while continuously maximizing
|
| 12 |
+
their radii for the current positions. This allows the system to
|
| 13 |
+
settle into a more optimal, tightly packed configuration.
|
| 14 |
+
|
| 15 |
+
Returns:
|
| 16 |
+
Tuple of (centers, radii)
|
| 17 |
+
centers: np.array of shape (26, 2) with (x, y) coordinates
|
| 18 |
+
radii: np.array of shape (26) with radius of each circle
|
| 19 |
+
"""
|
| 20 |
+
n = 26
|
| 21 |
+
centers = np.zeros((n, 2))
|
| 22 |
+
|
| 23 |
+
# Initial placement: a slightly perturbed grid of 5x5 circles + 1 extra
|
| 24 |
+
# This provides a reasonable starting distribution for the simulation.
|
| 25 |
+
grid_side = 5
|
| 26 |
+
spacing = 1.0 / (grid_side + 1) # Distribute centers from 1/6 to 5/6
|
| 27 |
+
|
| 28 |
+
count = 0
|
| 29 |
+
for i in range(grid_side):
|
| 30 |
+
for j in range(grid_side):
|
| 31 |
+
if count < n: # Place up to 25 circles in the 5x5 grid
|
| 32 |
+
centers[count] = [(i + 1) * spacing, (j + 1) * spacing]
|
| 33 |
+
count += 1
|
| 34 |
+
|
| 35 |
+
# Place the 26th circle. For 26, one remains after 5x5 grid (25 circles).
|
| 36 |
+
if count < n:
|
| 37 |
+
centers[count] = [0.5, 0.5] # Place it at the center of the square
|
| 38 |
+
count += 1
|
| 39 |
+
|
| 40 |
+
# Add small random perturbation to initial positions to break symmetry.
|
| 41 |
+
# This helps the optimization escape local minima and explore more diverse configurations.
|
| 42 |
+
centers += np.random.uniform(-0.02, 0.02, centers.shape)
|
| 43 |
+
|
| 44 |
+
# Ensure initial centers are within the [0,1] bounds
|
| 45 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 46 |
+
|
| 47 |
+
# --- Force-directed simulation parameters ---
|
| 48 |
+
num_iterations_total = 2500 # Total number of simulation steps
|
| 49 |
+
initial_learning_rate = 0.01 # Initial step size for moving centers
|
| 50 |
+
k_rep = 0.05 # Strength of circle-circle repulsion
|
| 51 |
+
k_wall = 0.1 # Strength of wall repulsion
|
| 52 |
+
|
| 53 |
+
# The learning rate decays over time, simulating an annealing process.
|
| 54 |
+
# This allows for larger movements initially and finer adjustments later.
|
| 55 |
+
current_learning_rate = initial_learning_rate
|
| 56 |
+
|
| 57 |
+
for iteration in range(num_iterations_total):
|
| 58 |
+
# 1. Compute radii for current centers.
|
| 59 |
+
# This function aims to maximize radii such that no circles overlap
|
| 60 |
+
# and all are within bounds for the given center positions.
|
| 61 |
+
radii = compute_max_radii_iterative(centers)
|
| 62 |
+
|
| 63 |
+
# 2. Calculate forces on centers.
|
| 64 |
+
# These forces push circles apart if they overlap or are too close to walls.
|
| 65 |
+
forces = np.zeros((n, 2))
|
| 66 |
+
|
| 67 |
+
for i in range(n):
|
| 68 |
+
# Wall repulsion: push circles away from boundaries if their radius extends beyond.
|
| 69 |
+
x, y = centers[i]
|
| 70 |
+
r = radii[i]
|
| 71 |
+
|
| 72 |
+
if x - r < 0: forces[i, 0] += k_wall * (r - x)
|
| 73 |
+
if x + r > 1: forces[i, 0] -= k_wall * ((x + r) - 1)
|
| 74 |
+
if y - r < 0: forces[i, 1] += k_wall * (r - y)
|
| 75 |
+
if y + r > 1: forces[i, 1] -= k_wall * ((y + r) - 1)
|
| 76 |
+
|
| 77 |
+
# Circle-circle repulsion: push overlapping circles apart.
|
| 78 |
+
for j in range(i + 1, n):
|
| 79 |
+
vec = centers[i] - centers[j]
|
| 80 |
+
dist = np.linalg.norm(vec)
|
| 81 |
+
|
| 82 |
+
min_separation = radii[i] + radii[j]
|
| 83 |
+
if dist < min_separation + 1e-9: # Overlap detected (with small tolerance)
|
| 84 |
+
overlap_amount = min_separation - dist
|
| 85 |
+
if dist > 1e-9: # Avoid division by zero if centers are too close
|
| 86 |
+
force_magnitude = k_rep * overlap_amount / dist
|
| 87 |
+
forces[i] += force_magnitude * vec
|
| 88 |
+
forces[j] -= force_magnitude * vec
|
| 89 |
+
else: # Centers are virtually identical, push in opposite random directions
|
| 90 |
+
random_vec = np.random.uniform(-1, 1, 2)
|
| 91 |
+
random_vec /= np.linalg.norm(random_vec)
|
| 92 |
+
forces[i] += k_rep * overlap_amount * random_vec
|
| 93 |
+
forces[j] -= k_rep * overlap_amount * random_vec
|
| 94 |
+
|
| 95 |
+
# 3. Update centers based on calculated forces.
|
| 96 |
+
centers += current_learning_rate * forces
|
| 97 |
+
|
| 98 |
+
# 4. Decay learning rate for annealing.
|
| 99 |
+
current_learning_rate = initial_learning_rate * (1 - iteration / num_iterations_total)
|
| 100 |
+
current_learning_rate = max(current_learning_rate, initial_learning_rate * 0.01) # Maintain a minimum step size
|
| 101 |
+
|
| 102 |
+
# 5. Clip centers to ensure they stay within the unit square.
|
| 103 |
+
# This is important for `compute_max_radii_iterative` to correctly calculate boundary limits.
|
| 104 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 105 |
+
|
| 106 |
+
# Final computation of radii after the simulation has settled.
|
| 107 |
+
final_radii = compute_max_radii_iterative(centers)
|
| 108 |
+
|
| 109 |
+
return centers, final_radii
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def compute_max_radii_iterative(centers):
|
| 113 |
+
"""
|
| 114 |
+
Computes the maximum possible radii for given circle centers such that all
|
| 115 |
+
circles are disjoint and contained within the unit square. This is an
|
| 116 |
+
iterative relaxation method that ensures convergence by repeatedly
|
| 117 |
+
checking and resolving overlaps.
|
| 118 |
+
|
| 119 |
+
Args:
|
| 120 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates of circle centers.
|
| 121 |
+
|
| 122 |
+
Returns:
|
| 123 |
+
np.array of shape (n) with the radius of each circle.
|
| 124 |
+
"""
|
| 125 |
+
n = centers.shape[0]
|
| 126 |
+
radii = np.zeros(n)
|
| 127 |
+
|
| 128 |
+
# Initialize radii based on the maximum possible distance to the nearest boundary.
|
| 129 |
+
# This ensures circles start as large as possible given only boundary constraints.
|
| 130 |
+
for i in range(n):
|
| 131 |
+
x, y = centers[i]
|
| 132 |
+
radii[i] = min(x, 1 - x, y, 1 - y)
|
| 133 |
+
radii[i] = max(radii[i], 1e-9) # Ensure a small positive radius to start.
|
| 134 |
+
|
| 135 |
+
# Iteratively adjust radii to prevent overlaps between circles.
|
| 136 |
+
# The loop runs for a fixed number of iterations to allow changes to propagate
|
| 137 |
+
# through the system until a stable state (or near-stable) is reached.
|
| 138 |
+
max_radii_solver_iterations = 200
|
| 139 |
+
|
| 140 |
+
for _ in range(max_radii_solver_iterations):
|
| 141 |
+
changed = False
|
| 142 |
+
for i in range(n):
|
| 143 |
+
# Re-check boundary conditions as radii might have been adjusted for overlaps.
|
| 144 |
+
x, y = centers[i]
|
| 145 |
+
boundary_limit = min(x, 1 - x, y, 1 - y)
|
| 146 |
+
if radii[i] > boundary_limit + 1e-9: # If radius exceeds boundary limit
|
| 147 |
+
radii[i] = boundary_limit
|
| 148 |
+
changed = True
|
| 149 |
+
|
| 150 |
+
# Check for overlaps with all other circles.
|
| 151 |
+
for j in range(n):
|
| 152 |
+
if i == j: continue
|
| 153 |
+
|
| 154 |
+
dist = np.linalg.norm(centers[i] - centers[j])
|
| 155 |
+
|
| 156 |
+
# If circles i and j overlap (with a small tolerance)
|
| 157 |
+
if radii[i] + radii[j] > dist + 1e-9:
|
| 158 |
+
sum_current_radii = radii[i] + radii[j]
|
| 159 |
+
if sum_current_radii > 1e-9: # Avoid division by zero
|
| 160 |
+
# Scale both radii proportionally so they just touch.
|
| 161 |
+
scale = dist / sum_current_radii
|
| 162 |
+
radii[i] *= scale
|
| 163 |
+
radii[j] *= scale
|
| 164 |
+
changed = True
|
| 165 |
+
else: # Both radii are near zero and centers are close. Assign tiny non-overlapping radii.
|
| 166 |
+
radii[i] = dist / 2.0 - 1e-10
|
| 167 |
+
radii[j] = dist / 2.0 - 1e-10
|
| 168 |
+
radii[i] = max(radii[i], 1e-9)
|
| 169 |
+
radii[j] = max(radii[j], 1e-9)
|
| 170 |
+
changed = True
|
| 171 |
+
if not changed:
|
| 172 |
+
break # If no radii were adjusted in an iteration, we've converged.
|
| 173 |
+
|
| 174 |
+
# Final pass to ensure all radii are non-negative and strictly within boundary limits.
|
| 175 |
+
for i in range(n):
|
| 176 |
+
x, y = centers[i]
|
| 177 |
+
radii[i] = max(0.0, radii[i]) # Radii must be non-negative.
|
| 178 |
+
radii[i] = min(radii[i], x, 1 - x, y, 1 - y) # Final clamp to boundaries.
|
| 179 |
+
|
| 180 |
+
return 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_gen50_period5_20260206_004532/gen_11/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/results/metrics.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 1.5704909843297161,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 1.5704909843297161,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.5006, 0.4867)\n centers[1] = (0.7016, 0.5048)\n centers[2] = (0.6610, 0.6398)\n centers[3] = (0.4808, 0.7204)\n centers[4] = (0.3446, 0.6649)\n centers[5] = (0.2864, 0.4873)\n centers[6] = (0.3633, 0.3514)\n centers[7] = (0.5160, 0.2985)\n centers[8] = (0.6560, 0.3259)\n centers[9] = (0.9230, 0.4893)\n centers[10] = (0.8795, 0.6707)\n centers[11] = (0.8141, 0.7999)\n centers[12] = (0.6934, 0.8602)\n centers[13] = (0.5364, 0.9176)\n centers[14] = (0.3957, 0.9230)\n centers[15] = (0.2371, 0.8509)\n centers[16] = (0.1458, 0.7343)\n centers[17] = (0.0873, 0.5629)\n centers[18] = (0.0673, 0.4269)\n centers[19] = (0.1371, 0.2779)\n centers[20] = (0.2511, 0.1496)\n centers[21] = (0.3879, 0.1149)\n centers[22] = (0.5403, 0.0896)\n centers[23] = (0.6726, 0.1246)\n centers[24] = (0.8016, 0.2253)\n centers[25] = (0.8818, 0.3546)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 1.5704909843297161
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 153.99051043763757,
|
| 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 |
+
"radii_std_dev": 0.016223133637346798,
|
| 23 |
+
"radii_min": 0.003852902524274984,
|
| 24 |
+
"radii_max": 0.08368869600380827,
|
| 25 |
+
"radii_median": 0.05914822721373934,
|
| 26 |
+
"total_area_covered": 0.3195191143296471,
|
| 27 |
+
"avg_boundary_proximity": 0.13442151186604837
|
| 28 |
+
},
|
| 29 |
+
"auxiliary_descriptions": {
|
| 30 |
+
"radii_std_dev": "Standard deviation of the radii of all 26 circles. A higher value indicates greater diversity in circle sizes, which might be beneficial for fitting into complex spaces.",
|
| 31 |
+
"radii_min": "The minimum radius among all circles. A very small value could indicate a strategy to fill tiny gaps, while a larger value suggests a preference for more uniform, larger circles.",
|
| 32 |
+
"radii_max": "The maximum radius among all circles. A large max radius implies one or more dominant circles, while a smaller max radius indicates more evenly sized circles.",
|
| 33 |
+
"total_area_covered": "The sum of the areas of all 26 circles. This metric provides a direct measure of the total space occupied within the unit square. Higher values indicate more efficient use of space. Since the primary score is `sum(radii)`, `total_area_covered` (proportional to `sum(radii^2)`) offers a slightly different perspective on packing density.",
|
| 34 |
+
"min_boundary_distance": "The minimum distance from the edge of any circle to any of the four boundaries of the unit square. A smaller value indicates that circles are packed closer to the container's edges, suggesting more efficient use of the available packing area and potentially denser solutions.",
|
| 35 |
+
"avg_boundary_distance": "The average minimum distance from the edge of each circle to its closest boundary. This provides a smoother measure of how well, on average, the circles are utilizing the space near the boundaries.",
|
| 36 |
+
"radii_median": "The median radius among all circles. Provides a measure of the central tendency of circle sizes, less sensitive to outliers than the mean.",
|
| 37 |
+
"avg_boundary_proximity": "The average of the minimum distances from each circle's edge to any of the four boundaries of the unit square. Lower values indicate that circles are placed closer to the edges, suggesting better utilization of the square's perimeter and potentially more efficient packing."
|
| 38 |
+
},
|
| 39 |
+
"timestamp": 1770340100.39303,
|
| 40 |
+
"generation": 11
|
| 41 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_11/rewrite.txt
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
def construct_packing():
|
| 5 |
+
"""
|
| 6 |
+
Construct an arrangement of 26 circles in a unit square
|
| 7 |
+
using a force-directed simulation to maximize the sum of their radii.
|
| 8 |
+
|
| 9 |
+
This algorithm starts with an initial concentric ring placement and iteratively
|
| 10 |
+
adjusts their positions based on repulsive forces. This hybrid approach
|
| 11 |
+
leverages a structured start to guide the optimization to a better result.
|
| 12 |
+
|
| 13 |
+
Returns:
|
| 14 |
+
Tuple of (centers, radii)
|
| 15 |
+
centers: np.array of shape (26, 2) with (x, y) coordinates
|
| 16 |
+
radii: np.array of shape (26) with radius of each circle
|
| 17 |
+
"""
|
| 18 |
+
n = 26
|
| 19 |
+
centers = np.zeros((n, 2))
|
| 20 |
+
|
| 21 |
+
# Initial placement: Concentric rings (1 + 8 + 17).
|
| 22 |
+
# This provides a structured, non-grid starting point that can lead
|
| 23 |
+
# to better packing configurations. The radii are chosen to keep all
|
| 24 |
+
# circles well within the unit square initially.
|
| 25 |
+
|
| 26 |
+
# Central circle
|
| 27 |
+
centers[0] = [0.5, 0.5]
|
| 28 |
+
|
| 29 |
+
# Inner ring of 8 circles
|
| 30 |
+
r_inner = 0.22 # Radius of the inner ring circle centers
|
| 31 |
+
num_inner = 8
|
| 32 |
+
for i in range(num_inner):
|
| 33 |
+
angle = 2 * np.pi * i / num_inner
|
| 34 |
+
centers[i + 1] = [0.5 + r_inner * np.cos(angle), 0.5 + r_inner * np.sin(angle)]
|
| 35 |
+
|
| 36 |
+
# Outer ring of 17 circles
|
| 37 |
+
r_outer = 0.42 # Radius of the outer ring circle centers
|
| 38 |
+
num_outer = 17
|
| 39 |
+
for i in range(num_outer):
|
| 40 |
+
angle = 2 * np.pi * i / num_outer
|
| 41 |
+
# The offset is 1 (center) + num_inner
|
| 42 |
+
centers[i + 1 + num_inner] = [0.5 + r_outer * np.cos(angle), 0.5 + r_outer * np.sin(angle)]
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
# Add small random perturbation to initial positions to break symmetry.
|
| 46 |
+
# This helps the optimization escape local minima and explore more diverse configurations.
|
| 47 |
+
centers += np.random.uniform(-0.02, 0.02, centers.shape)
|
| 48 |
+
|
| 49 |
+
# Ensure initial centers are within the [0,1] bounds
|
| 50 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 51 |
+
|
| 52 |
+
# --- Force-directed simulation parameters ---
|
| 53 |
+
num_iterations_total = 2500 # Total number of simulation steps
|
| 54 |
+
initial_learning_rate = 0.01 # Initial step size for moving centers
|
| 55 |
+
k_rep = 0.05 # Strength of circle-circle repulsion
|
| 56 |
+
k_wall = 0.1 # Strength of wall repulsion
|
| 57 |
+
|
| 58 |
+
# The learning rate decays over time, simulating an annealing process.
|
| 59 |
+
# This allows for larger movements initially and finer adjustments later.
|
| 60 |
+
current_learning_rate = initial_learning_rate
|
| 61 |
+
|
| 62 |
+
for iteration in range(num_iterations_total):
|
| 63 |
+
# 1. Compute radii for current centers.
|
| 64 |
+
radii = compute_max_radii_iterative(centers)
|
| 65 |
+
|
| 66 |
+
# 2. Calculate forces on centers.
|
| 67 |
+
forces = np.zeros((n, 2))
|
| 68 |
+
|
| 69 |
+
for i in range(n):
|
| 70 |
+
# Wall repulsion: push circles away from boundaries.
|
| 71 |
+
x, y = centers[i]
|
| 72 |
+
r = radii[i]
|
| 73 |
+
|
| 74 |
+
if x - r < 0: forces[i, 0] += k_wall * (r - x)
|
| 75 |
+
if x + r > 1: forces[i, 0] -= k_wall * ((x + r) - 1)
|
| 76 |
+
if y - r < 0: forces[i, 1] += k_wall * (r - y)
|
| 77 |
+
if y + r > 1: forces[i, 1] -= k_wall * ((y + r) - 1)
|
| 78 |
+
|
| 79 |
+
# Circle-circle repulsion: push overlapping circles apart.
|
| 80 |
+
for j in range(i + 1, n):
|
| 81 |
+
vec = centers[i] - centers[j]
|
| 82 |
+
dist = np.linalg.norm(vec)
|
| 83 |
+
|
| 84 |
+
min_separation = radii[i] + radii[j]
|
| 85 |
+
if dist < min_separation + 1e-9: # Overlap detected
|
| 86 |
+
overlap_amount = min_separation - dist
|
| 87 |
+
if dist > 1e-9:
|
| 88 |
+
force_magnitude = k_rep * overlap_amount / dist
|
| 89 |
+
forces[i] += force_magnitude * vec
|
| 90 |
+
forces[j] -= force_magnitude * vec
|
| 91 |
+
else: # Centers are virtually identical
|
| 92 |
+
random_vec = np.random.uniform(-1, 1, 2)
|
| 93 |
+
random_vec /= np.linalg.norm(random_vec)
|
| 94 |
+
forces[i] += k_rep * overlap_amount * random_vec
|
| 95 |
+
forces[j] -= k_rep * overlap_amount * random_vec
|
| 96 |
+
|
| 97 |
+
# 3. Update centers based on calculated forces.
|
| 98 |
+
centers += current_learning_rate * forces
|
| 99 |
+
|
| 100 |
+
# 4. Decay learning rate for annealing.
|
| 101 |
+
current_learning_rate = initial_learning_rate * (1 - iteration / num_iterations_total)
|
| 102 |
+
current_learning_rate = max(current_learning_rate, initial_learning_rate * 0.01)
|
| 103 |
+
|
| 104 |
+
# 5. Clip centers to ensure they stay within the unit square.
|
| 105 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 106 |
+
|
| 107 |
+
# Final computation of radii after the simulation has settled.
|
| 108 |
+
final_radii = compute_max_radii_iterative(centers)
|
| 109 |
+
|
| 110 |
+
return centers, final_radii
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def compute_max_radii_iterative(centers):
|
| 114 |
+
"""
|
| 115 |
+
Computes the maximum possible radii for given circle centers such that all
|
| 116 |
+
circles are disjoint and contained within the unit square. This is an
|
| 117 |
+
iterative relaxation method.
|
| 118 |
+
|
| 119 |
+
Args:
|
| 120 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates of circle centers.
|
| 121 |
+
|
| 122 |
+
Returns:
|
| 123 |
+
np.array of shape (n) with the radius of each circle.
|
| 124 |
+
"""
|
| 125 |
+
n = centers.shape[0]
|
| 126 |
+
radii = np.zeros(n)
|
| 127 |
+
|
| 128 |
+
# Initialize radii based on the maximum possible distance to the nearest boundary.
|
| 129 |
+
for i in range(n):
|
| 130 |
+
x, y = centers[i]
|
| 131 |
+
radii[i] = min(x, 1 - x, y, 1 - y)
|
| 132 |
+
radii[i] = max(radii[i], 1e-9)
|
| 133 |
+
|
| 134 |
+
# Iteratively adjust radii to prevent overlaps between circles.
|
| 135 |
+
max_radii_solver_iterations = 200
|
| 136 |
+
|
| 137 |
+
for _ in range(max_radii_solver_iterations):
|
| 138 |
+
changed = False
|
| 139 |
+
for i in range(n):
|
| 140 |
+
# Re-check boundary conditions.
|
| 141 |
+
x, y = centers[i]
|
| 142 |
+
boundary_limit = min(x, 1 - x, y, 1 - y)
|
| 143 |
+
if radii[i] > boundary_limit + 1e-9:
|
| 144 |
+
radii[i] = boundary_limit
|
| 145 |
+
changed = True
|
| 146 |
+
|
| 147 |
+
# Check for overlaps with all other circles.
|
| 148 |
+
for j in range(n):
|
| 149 |
+
if i == j: continue
|
| 150 |
+
|
| 151 |
+
dist = np.linalg.norm(centers[i] - centers[j])
|
| 152 |
+
|
| 153 |
+
if radii[i] + radii[j] > dist + 1e-9:
|
| 154 |
+
sum_current_radii = radii[i] + radii[j]
|
| 155 |
+
if sum_current_radii > 1e-9:
|
| 156 |
+
# Scale both radii proportionally so they just touch.
|
| 157 |
+
scale = dist / sum_current_radii
|
| 158 |
+
radii[i] *= scale
|
| 159 |
+
radii[j] *= scale
|
| 160 |
+
changed = True
|
| 161 |
+
else:
|
| 162 |
+
radii[i] = dist / 2.0 - 1e-10
|
| 163 |
+
radii[j] = dist / 2.0 - 1e-10
|
| 164 |
+
radii[i] = max(radii[i], 1e-9)
|
| 165 |
+
radii[j] = max(radii[j], 1e-9)
|
| 166 |
+
changed = True
|
| 167 |
+
if not changed:
|
| 168 |
+
break
|
| 169 |
+
|
| 170 |
+
# Final pass to ensure all radii are non-negative and strictly within boundary limits.
|
| 171 |
+
for i in range(n):
|
| 172 |
+
x, y = centers[i]
|
| 173 |
+
radii[i] = max(0.0, radii[i])
|
| 174 |
+
radii[i] = min(radii[i], x, 1 - x, y, 1 - y)
|
| 175 |
+
|
| 176 |
+
return radii
|
| 177 |
+
# EVOLVE-BLOCK-END
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_12/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (6.11 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_12/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_12/results/metrics.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 1.5249779496544622,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 1.5249779496544622,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.2000, 0.2000)\n centers[1] = (0.2000, 0.4000)\n centers[2] = (0.2000, 0.6000)\n centers[3] = (0.2000, 0.8000)\n centers[4] = (0.4000, 0.2000)\n centers[5] = (0.4000, 0.4000)\n centers[6] = (0.4000, 0.6000)\n centers[7] = (0.4000, 0.8000)\n centers[8] = (0.6000, 0.2000)\n centers[9] = (0.6000, 0.4000)\n centers[10] = (0.6000, 0.6000)\n centers[11] = (0.6000, 0.8000)\n centers[12] = (0.8000, 0.2000)\n centers[13] = (0.8000, 0.4000)\n centers[14] = (0.8000, 0.6000)\n centers[15] = (0.8000, 0.8000)\n centers[16] = (0.0500, 0.0500)\n centers[17] = (0.0500, 0.9500)\n centers[18] = (0.9500, 0.0500)\n centers[19] = (0.9500, 0.9500)\n centers[20] = (0.3333, 0.9500)\n centers[21] = (0.6667, 0.9500)\n centers[22] = (0.3333, 0.0500)\n centers[23] = (0.6667, 0.0500)\n centers[24] = (0.0500, 0.5000)\n centers[25] = (0.9500, 0.5000)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 1.5249779496544622
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 131.75387663114816,
|
| 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 |
+
"radii_std_dev": 0.019222947767118834,
|
| 23 |
+
"radii_min": 0.025759328705441707,
|
| 24 |
+
"radii_max": 0.10661178448914414,
|
| 25 |
+
"radii_median": 0.050000000000000044,
|
| 26 |
+
"total_area_covered": 0.31118132916948127,
|
| 27 |
+
"avg_boundary_proximity": 0.1144239250132899
|
| 28 |
+
},
|
| 29 |
+
"auxiliary_descriptions": {
|
| 30 |
+
"radii_std_dev": "Standard deviation of the radii of all 26 circles. A higher value indicates greater diversity in circle sizes, which might be beneficial for fitting into complex spaces.",
|
| 31 |
+
"radii_min": "The minimum radius among all circles. A very small value could indicate a strategy to fill tiny gaps, while a larger value suggests a preference for more uniform, larger circles.",
|
| 32 |
+
"radii_max": "The maximum radius among all circles. A large max radius implies one or more dominant circles, while a smaller max radius indicates more evenly sized circles.",
|
| 33 |
+
"total_area_covered": "The sum of the areas of all 26 circles. This metric provides a direct measure of the total space occupied within the unit square. Higher values indicate more efficient use of space. Since the primary score is `sum(radii)`, `total_area_covered` (proportional to `sum(radii^2)`) offers a slightly different perspective on packing density.",
|
| 34 |
+
"min_boundary_distance": "The minimum distance from the edge of any circle to any of the four boundaries of the unit square. A smaller value indicates that circles are packed closer to the container's edges, suggesting more efficient use of the available packing area and potentially denser solutions.",
|
| 35 |
+
"avg_boundary_distance": "The average minimum distance from the edge of each circle to its closest boundary. This provides a smoother measure of how well, on average, the circles are utilizing the space near the boundaries.",
|
| 36 |
+
"radii_median": "The median radius among all circles. Provides a measure of the central tendency of circle sizes, less sensitive to outliers than the mean.",
|
| 37 |
+
"avg_boundary_proximity": "The average of the minimum distances from each circle's edge to any of the four boundaries of the unit square. Lower values indicate that circles are placed closer to the edges, suggesting better utilization of the square's perimeter and potentially more efficient packing."
|
| 38 |
+
},
|
| 39 |
+
"timestamp": 1770340122.0232985,
|
| 40 |
+
"generation": 12
|
| 41 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_13/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (3.79 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_13/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": false,
|
| 3 |
+
"error": "Validation failed: Circles 0 & 1 overlap. Dist: 0.1667, Sum Radii: 0.1833"
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_13/results/metrics.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 3.2838433781420835,
|
| 3 |
+
"correct": false,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 3.2838433781420835,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.0833, 0.1000)\n centers[1] = (0.2500, 0.1000)\n centers[2] = (0.4167, 0.1000)\n centers[3] = (0.5833, 0.1000)\n centers[4] = (0.7500, 0.1000)\n centers[5] = (0.9167, 0.1000)\n centers[6] = (0.0833, 0.5000)\n centers[7] = (0.2500, 0.5000)\n centers[8] = (0.4167, 0.5000)\n centers[9] = (0.5833, 0.5000)\n centers[10] = (0.7500, 0.5000)\n centers[11] = (0.9167, 0.5000)\n centers[12] = (0.0833, 0.9000)\n centers[13] = (0.2500, 0.9000)\n centers[14] = (0.4167, 0.9000)\n centers[15] = (0.5833, 0.9000)\n centers[16] = (0.7500, 0.9000)\n centers[17] = (0.9167, 0.9000)\n centers[18] = (0.1250, 0.3000)\n centers[19] = (0.3750, 0.3000)\n centers[20] = (0.6250, 0.3000)\n centers[21] = (0.8750, 0.3000)\n centers[22] = (0.1250, 0.7000)\n centers[23] = (0.3750, 0.7000)\n centers[24] = (0.6250, 0.7000)\n centers[25] = (0.8750, 0.7000)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 3.2838433781420835
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 0.9332031589001417,
|
| 14 |
+
"execution_time_std": 0.0,
|
| 15 |
+
"num_valid_runs": 0,
|
| 16 |
+
"num_invalid_runs": 1,
|
| 17 |
+
"all_validation_errors": [
|
| 18 |
+
"Circles 0 & 1 overlap. Dist: 0.1667, Sum Radii: 0.1833"
|
| 19 |
+
],
|
| 20 |
+
"correct": false,
|
| 21 |
+
"validation_error": "Validation failed: Circles 0 & 1 overlap. Dist: 0.1667, Sum Radii: 0.1833"
|
| 22 |
+
},
|
| 23 |
+
"auxiliary": {
|
| 24 |
+
"radii_std_dev": 0.04272781693066007,
|
| 25 |
+
"radii_min": 0.08333333333333333,
|
| 26 |
+
"radii_max": 0.20429417786885432,
|
| 27 |
+
"radii_median": 0.1,
|
| 28 |
+
"total_area_covered": 1.4521139376592815,
|
| 29 |
+
"avg_boundary_proximity": 0.040364998276586524
|
| 30 |
+
},
|
| 31 |
+
"auxiliary_descriptions": {
|
| 32 |
+
"radii_std_dev": "Standard deviation of the radii of all 26 circles. A higher value indicates greater diversity in circle sizes, which might be beneficial for fitting into complex spaces.",
|
| 33 |
+
"radii_min": "The minimum radius among all circles. A very small value could indicate a strategy to fill tiny gaps, while a larger value suggests a preference for more uniform, larger circles.",
|
| 34 |
+
"radii_max": "The maximum radius among all circles. A large max radius implies one or more dominant circles, while a smaller max radius indicates more evenly sized circles.",
|
| 35 |
+
"total_area_covered": "The sum of the areas of all 26 circles. This metric provides a direct measure of the total space occupied within the unit square. Higher values indicate more efficient use of space. Since the primary score is `sum(radii)`, `total_area_covered` (proportional to `sum(radii^2)`) offers a slightly different perspective on packing density.",
|
| 36 |
+
"min_boundary_distance": "The minimum distance from the edge of any circle to any of the four boundaries of the unit square. A smaller value indicates that circles are packed closer to the container's edges, suggesting more efficient use of the available packing area and potentially denser solutions.",
|
| 37 |
+
"avg_boundary_distance": "The average minimum distance from the edge of each circle to its closest boundary. This provides a smoother measure of how well, on average, the circles are utilizing the space near the boundaries.",
|
| 38 |
+
"radii_median": "The median radius among all circles. Provides a measure of the central tendency of circle sizes, less sensitive to outliers than the mean.",
|
| 39 |
+
"avg_boundary_proximity": "The average of the minimum distances from each circle's edge to any of the four boundaries of the unit square. Lower values indicate that circles are placed closer to the edges, suggesting better utilization of the square's perimeter and potentially more efficient packing."
|
| 40 |
+
},
|
| 41 |
+
"timestamp": 1770340132.4006386,
|
| 42 |
+
"generation": 13
|
| 43 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_14/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (3.63 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_14/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_14/results/metrics.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 2.128334992592051,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 2.128334992592051,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.0833, 0.0833)\n centers[1] = (0.2500, 0.0833)\n centers[2] = (0.4167, 0.0833)\n centers[3] = (0.5833, 0.0833)\n centers[4] = (0.7500, 0.0833)\n centers[5] = (0.9167, 0.0833)\n centers[6] = (0.0833, 0.5000)\n centers[7] = (0.2500, 0.5000)\n centers[8] = (0.4167, 0.5000)\n centers[9] = (0.5833, 0.5000)\n centers[10] = (0.7500, 0.5000)\n centers[11] = (0.9167, 0.5000)\n centers[12] = (0.0833, 0.9167)\n centers[13] = (0.2500, 0.9167)\n centers[14] = (0.4167, 0.9167)\n centers[15] = (0.5833, 0.9167)\n centers[16] = (0.7500, 0.9167)\n centers[17] = (0.9167, 0.9167)\n centers[18] = (0.1250, 0.2917)\n centers[19] = (0.3750, 0.2917)\n centers[20] = (0.6250, 0.2917)\n centers[21] = (0.8750, 0.2917)\n centers[22] = (0.1250, 0.7083)\n centers[23] = (0.3750, 0.7083)\n centers[24] = (0.6250, 0.7083)\n centers[25] = (0.8750, 0.7083)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 2.128334992592051
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 0.0026385830715298653,
|
| 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 |
+
"radii_std_dev": 0.03105755712714461,
|
| 23 |
+
"radii_min": 0.03063458959602766,
|
| 24 |
+
"radii_max": 0.12769638565459998,
|
| 25 |
+
"radii_median": 0.08254701690258703,
|
| 26 |
+
"total_area_covered": 0.626126715770616,
|
| 27 |
+
"avg_boundary_proximity": 0.07839737207979293
|
| 28 |
+
},
|
| 29 |
+
"auxiliary_descriptions": {
|
| 30 |
+
"radii_std_dev": "Standard deviation of the radii of all 26 circles. A higher value indicates greater diversity in circle sizes, which might be beneficial for fitting into complex spaces.",
|
| 31 |
+
"radii_min": "The minimum radius among all circles. A very small value could indicate a strategy to fill tiny gaps, while a larger value suggests a preference for more uniform, larger circles.",
|
| 32 |
+
"radii_max": "The maximum radius among all circles. A large max radius implies one or more dominant circles, while a smaller max radius indicates more evenly sized circles.",
|
| 33 |
+
"total_area_covered": "The sum of the areas of all 26 circles. This metric provides a direct measure of the total space occupied within the unit square. Higher values indicate more efficient use of space. Since the primary score is `sum(radii)`, `total_area_covered` (proportional to `sum(radii^2)`) offers a slightly different perspective on packing density.",
|
| 34 |
+
"min_boundary_distance": "The minimum distance from the edge of any circle to any of the four boundaries of the unit square. A smaller value indicates that circles are packed closer to the container's edges, suggesting more efficient use of the available packing area and potentially denser solutions.",
|
| 35 |
+
"avg_boundary_distance": "The average minimum distance from the edge of each circle to its closest boundary. This provides a smoother measure of how well, on average, the circles are utilizing the space near the boundaries.",
|
| 36 |
+
"radii_median": "The median radius among all circles. Provides a measure of the central tendency of circle sizes, less sensitive to outliers than the mean.",
|
| 37 |
+
"avg_boundary_proximity": "The average of the minimum distances from each circle's edge to any of the four boundaries of the unit square. Lower values indicate that circles are placed closer to the edges, suggesting better utilization of the square's perimeter and potentially more efficient packing."
|
| 38 |
+
},
|
| 39 |
+
"timestamp": 1770340196.2545774,
|
| 40 |
+
"generation": 14
|
| 41 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_15/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (3.47 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_15/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_15/results/metrics.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 1.7171970600776312,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 1.7171970600776312,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.0833, 0.1000)\n centers[1] = (0.2500, 0.1000)\n centers[2] = (0.4167, 0.1000)\n centers[3] = (0.5833, 0.1000)\n centers[4] = (0.7500, 0.1000)\n centers[5] = (0.9167, 0.1000)\n centers[6] = (0.0833, 0.5000)\n centers[7] = (0.2500, 0.5000)\n centers[8] = (0.4167, 0.5000)\n centers[9] = (0.5833, 0.5000)\n centers[10] = (0.7500, 0.5000)\n centers[11] = (0.9167, 0.5000)\n centers[12] = (0.0833, 0.9000)\n centers[13] = (0.2500, 0.9000)\n centers[14] = (0.4167, 0.9000)\n centers[15] = (0.5833, 0.9000)\n centers[16] = (0.7500, 0.9000)\n centers[17] = (0.9167, 0.9000)\n centers[18] = (0.1667, 0.3000)\n centers[19] = (0.3333, 0.3000)\n centers[20] = (0.6667, 0.3000)\n centers[21] = (0.8333, 0.3000)\n centers[22] = (0.1667, 0.7000)\n centers[23] = (0.3333, 0.7000)\n centers[24] = (0.6667, 0.7000)\n centers[25] = (0.8333, 0.7000)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 1.7171970600776312
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 0.002858915366232395,
|
| 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 |
+
"total_packed_area_ratio": 0.38148534647859283,
|
| 23 |
+
"max_boundary_violation": 0.0,
|
| 24 |
+
"max_overlap_magnitude": 0.0,
|
| 25 |
+
"radii_mean": 0.06604604077221658,
|
| 26 |
+
"radii_std_dev": 0.017559229629333418,
|
| 27 |
+
"radii_min": 0.028697245595737074,
|
| 28 |
+
"radii_max": 0.08629007773736326
|
| 29 |
+
},
|
| 30 |
+
"auxiliary_descriptions": {
|
| 31 |
+
"total_packed_area_ratio": "Sum of the areas of all circles (pi * r^2) divided by the unit square area (1). Higher values indicate better space utilization. This complements `sum_of_radii` by providing an area-based density.",
|
| 32 |
+
"max_boundary_violation": "The maximum distance by which any circle extends outside the [0,1] unit square. A value of 0 indicates no boundary violations. For invalid solutions, this quantifies the severity of the worst boundary constraint failure.",
|
| 33 |
+
"max_overlap_magnitude": "The maximum overlap distance between any two circles. A value of 0 indicates no overlaps. For invalid solutions, this quantifies the severity of the worst overlap constraint failure.",
|
| 34 |
+
"radii_mean": "The average radius of all circles. Useful for tracking the typical size of circles.",
|
| 35 |
+
"radii_std_dev": "Standard deviation of the radii of all 26 circles. A higher value indicates greater diversity in circle sizes, which might be beneficial for fitting into complex spaces.",
|
| 36 |
+
"radii_min": "The minimum radius among all circles. A very small value could indicate a strategy to fill tiny gaps, while a larger value suggests a preference for more uniform, larger circles.",
|
| 37 |
+
"radii_max": "The maximum radius among all circles. A large max radius implies one or more dominant circles, while a smaller max radius indicates more evenly sized circles.",
|
| 38 |
+
"total_area_covered": "The sum of the areas of all 26 circles. This metric provides a direct measure of the total space occupied within the unit square. Higher values indicate more efficient use of space. Since the primary score is `sum(radii)`, `total_area_covered` (proportional to `sum(radii^2)`) offers a slightly different perspective on packing density.",
|
| 39 |
+
"min_boundary_distance": "The minimum distance from the edge of any circle to any of the four boundaries of the unit square. A smaller value indicates that circles are packed closer to the container's edges, suggesting more efficient use of the available packing area and potentially denser solutions.",
|
| 40 |
+
"avg_boundary_distance": "The average minimum distance from the edge of each circle to its closest boundary. This provides a smoother measure of how well, on average, the circles are utilizing the space near the boundaries.",
|
| 41 |
+
"radii_median": "The median radius among all circles. Provides a measure of the central tendency of circle sizes, less sensitive to outliers than the mean.",
|
| 42 |
+
"avg_boundary_proximity": "The average of the minimum distances from each circle's edge to any of the four boundaries of the unit square. Lower values indicate that circles are placed closer to the edges, suggesting better utilization of the square's perimeter and potentially more efficient packing."
|
| 43 |
+
},
|
| 44 |
+
"timestamp": 1770340352.802615,
|
| 45 |
+
"generation": 15
|
| 46 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_16/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (6.41 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_16/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_16/results/metrics.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 0.2116089912340687,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 0.2116089912340687,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.2598, 0.2333)\n centers[1] = (0.7468, 0.7520)\n centers[2] = (0.2110, 0.3940)\n centers[3] = (0.1994, 0.6147)\n centers[4] = (0.4125, 0.1926)\n centers[5] = (0.3815, 0.8043)\n centers[6] = (0.6112, 0.2122)\n centers[7] = (0.6088, 0.8029)\n centers[8] = (0.8055, 0.4001)\n centers[9] = (0.8178, 0.6075)\n centers[10] = (0.0954, 0.2898)\n centers[11] = (0.0990, 0.7043)\n centers[12] = (0.3012, 0.1164)\n centers[13] = (0.2959, 0.8935)\n centers[14] = (0.7139, 0.0943)\n centers[15] = (0.7071, 0.8887)\n centers[16] = (0.8850, 0.3016)\n centers[17] = (0.8939, 0.6845)\n centers[18] = (0.3106, 0.4406)\n centers[19] = (0.3169, 0.5509)\n centers[20] = (0.4493, 0.3049)\n centers[21] = (0.4390, 0.6898)\n centers[22] = (0.5447, 0.2837)\n centers[23] = (0.5540, 0.6942)\n centers[24] = (0.6946, 0.4664)\n centers[25] = (0.7058, 0.5544)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 0.2116089912340687
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 396.1333593428135,
|
| 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 |
+
"total_packed_area_ratio": 0.023412819524565844,
|
| 23 |
+
"max_boundary_violation": 0.0,
|
| 24 |
+
"max_overlap_magnitude": 0.0,
|
| 25 |
+
"radii_mean": 0.008138807355156489,
|
| 26 |
+
"radii_std_dev": 0.014845728605889801,
|
| 27 |
+
"radii_min": 0.0,
|
| 28 |
+
"radii_max": 0.058620449027382476
|
| 29 |
+
},
|
| 30 |
+
"auxiliary_descriptions": {
|
| 31 |
+
"total_packed_area_ratio": "Sum of the areas of all circles (pi * r^2) divided by the unit square area (1). Higher values indicate better space utilization. This complements `sum_of_radii` by providing an area-based density.",
|
| 32 |
+
"max_boundary_violation": "The maximum distance by which any circle extends outside the [0,1] unit square. A value of 0 indicates no boundary violations. For invalid solutions, this quantifies the severity of the worst boundary constraint failure.",
|
| 33 |
+
"max_overlap_magnitude": "The maximum overlap distance between any two circles. A value of 0 indicates no overlaps. For invalid solutions, this quantifies the severity of the worst overlap constraint failure.",
|
| 34 |
+
"radii_mean": "The average radius of all circles. Useful for tracking the typical size of circles.",
|
| 35 |
+
"radii_std_dev": "Standard deviation of the radii of all 26 circles. A higher value indicates greater diversity in circle sizes, which might be beneficial for fitting into complex spaces.",
|
| 36 |
+
"radii_min": "The minimum radius among all circles. A very small value could indicate a strategy to fill tiny gaps, while a larger value suggests a preference for more uniform, larger circles.",
|
| 37 |
+
"radii_max": "The maximum radius among all circles. A large max radius implies one or more dominant circles, while a smaller max radius indicates more evenly sized circles.",
|
| 38 |
+
"total_area_covered": "The sum of the areas of all 26 circles. This metric provides a direct measure of the total space occupied within the unit square. Higher values indicate more efficient use of space. Since the primary score is `sum(radii)`, `total_area_covered` (proportional to `sum(radii^2)`) offers a slightly different perspective on packing density.",
|
| 39 |
+
"min_boundary_distance": "The minimum distance from the edge of any circle to any of the four boundaries of the unit square. A smaller value indicates that circles are packed closer to the container's edges, suggesting more efficient use of the available packing area and potentially denser solutions.",
|
| 40 |
+
"avg_boundary_distance": "The average minimum distance from the edge of each circle to its closest boundary. This provides a smoother measure of how well, on average, the circles are utilizing the space near the boundaries.",
|
| 41 |
+
"radii_median": "The median radius among all circles. Provides a measure of the central tendency of circle sizes, less sensitive to outliers than the mean.",
|
| 42 |
+
"avg_boundary_proximity": "The average of the minimum distances from each circle's edge to any of the four boundaries of the unit square. Lower values indicate that circles are placed closer to the edges, suggesting better utilization of the square's perimeter and potentially more efficient packing."
|
| 43 |
+
},
|
| 44 |
+
"timestamp": 1770340838.8235772,
|
| 45 |
+
"generation": 16
|
| 46 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_17/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (6.38 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_17/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_17/results/metrics.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 1.6535350159867128,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 1.6535350159867128,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.0984, 0.0944)\n centers[1] = (0.3064, 0.0817)\n centers[2] = (0.5105, 0.1120)\n centers[3] = (0.6918, 0.1134)\n centers[4] = (0.9108, 0.1009)\n centers[5] = (0.0386, 0.2819)\n centers[6] = (0.2248, 0.2977)\n centers[7] = (0.4250, 0.3042)\n centers[8] = (0.6089, 0.2976)\n centers[9] = (0.7621, 0.2901)\n centers[10] = (0.9355, 0.2850)\n centers[11] = (0.1033, 0.5095)\n centers[12] = (0.3197, 0.4827)\n centers[13] = (0.5004, 0.5036)\n centers[14] = (0.7125, 0.5017)\n centers[15] = (0.8998, 0.5091)\n centers[16] = (0.0381, 0.6801)\n centers[17] = (0.2455, 0.7171)\n centers[18] = (0.3968, 0.7123)\n centers[19] = (0.5743, 0.7176)\n centers[20] = (0.7855, 0.6963)\n centers[21] = (0.9497, 0.7045)\n centers[22] = (0.2168, 0.9116)\n centers[23] = (0.3881, 0.9088)\n centers[24] = (0.5943, 0.9136)\n centers[25] = (0.8160, 0.9189)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 1.6535350159867128
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 99.09497617185116,
|
| 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 |
+
"total_packed_area_ratio": 0.369077616356679,
|
| 23 |
+
"max_boundary_violation": 0.0,
|
| 24 |
+
"max_overlap_magnitude": 0.0,
|
| 25 |
+
"radii_mean": 0.06359750061487357,
|
| 26 |
+
"radii_std_dev": 0.021768325489068974,
|
| 27 |
+
"radii_min": 0.032048000834821185,
|
| 28 |
+
"radii_max": 0.11269655266219117
|
| 29 |
+
},
|
| 30 |
+
"auxiliary_descriptions": {
|
| 31 |
+
"total_packed_area_ratio": "Sum of the areas of all circles (pi * r^2) divided by the unit square area (1). Higher values indicate better space utilization. This complements `sum_of_radii` by providing an area-based density.",
|
| 32 |
+
"max_boundary_violation": "The maximum distance by which any circle extends outside the [0,1] unit square. A value of 0 indicates no boundary violations. For invalid solutions, this quantifies the severity of the worst boundary constraint failure.",
|
| 33 |
+
"max_overlap_magnitude": "The maximum overlap distance between any two circles. A value of 0 indicates no overlaps. For invalid solutions, this quantifies the severity of the worst overlap constraint failure.",
|
| 34 |
+
"radii_mean": "The average radius of all circles. Useful for tracking the typical size of circles.",
|
| 35 |
+
"radii_std_dev": "Standard deviation of the radii of all 26 circles. A higher value indicates greater diversity in circle sizes, which might be beneficial for fitting into complex spaces.",
|
| 36 |
+
"radii_min": "The minimum radius among all circles. A very small value could indicate a strategy to fill tiny gaps, while a larger value suggests a preference for more uniform, larger circles.",
|
| 37 |
+
"radii_max": "The maximum radius among all circles. A large max radius implies one or more dominant circles, while a smaller max radius indicates more evenly sized circles.",
|
| 38 |
+
"total_area_covered": "The sum of the areas of all 26 circles. This metric provides a direct measure of the total space occupied within the unit square. Higher values indicate more efficient use of space. Since the primary score is `sum(radii)`, `total_area_covered` (proportional to `sum(radii^2)`) offers a slightly different perspective on packing density.",
|
| 39 |
+
"min_boundary_distance": "The minimum distance from the edge of any circle to any of the four boundaries of the unit square. A smaller value indicates that circles are packed closer to the container's edges, suggesting more efficient use of the available packing area and potentially denser solutions.",
|
| 40 |
+
"avg_boundary_distance": "The average minimum distance from the edge of each circle to its closest boundary. This provides a smoother measure of how well, on average, the circles are utilizing the space near the boundaries.",
|
| 41 |
+
"radii_median": "The median radius among all circles. Provides a measure of the central tendency of circle sizes, less sensitive to outliers than the mean.",
|
| 42 |
+
"avg_boundary_proximity": "The average of the minimum distances from each circle's edge to any of the four boundaries of the unit square. Lower values indicate that circles are placed closer to the edges, suggesting better utilization of the square's perimeter and potentially more efficient packing."
|
| 43 |
+
},
|
| 44 |
+
"timestamp": 1770340582.3671024,
|
| 45 |
+
"generation": 17
|
| 46 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_18/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (6.62 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen50_period5_20260206_004532/gen_18/edit.diff
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--- a/original.py
|
| 2 |
+
+++ b/original.py
|
| 3 |
+
@@ -1,208 +1,214 @@
|
| 4 |
+
# EVOLVE-BLOCK-START
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
def construct_packing():
|
| 8 |
+
"""
|
| 9 |
+
Construct an arrangement of 26 circles in a unit square
|
| 10 |
+
using a force-directed simulation to maximize the sum of their radii.
|
| 11 |
+
|
| 12 |
+
This algorithm starts with an initial circle placement and iteratively
|
| 13 |
+
adjusts their positions based on repulsive forces between overlapping
|
| 14 |
+
circles and from the square boundaries, while continuously maximizing
|
| 15 |
+
their radii for the current positions. This allows the system to
|
| 16 |
+
settle into a more optimal, tightly packed configuration.
|
| 17 |
+
|
| 18 |
+
Returns:
|
| 19 |
+
Tuple of (centers, radii)
|
| 20 |
+
centers: np.array of shape (26, 2) with (x, y) coordinates
|
| 21 |
+
radii: np.array of shape (26) with radius of each circle
|
| 22 |
+
"""
|
| 23 |
+
n = 26
|
| 24 |
+
centers = np.zeros((n, 2))
|
| 25 |
+
|
| 26 |
+
- # Initial placement: A 4-fold symmetric pattern (D4 symmetry).
|
| 27 |
+
- # For N=26, we use 3 seed points generating 8 points each (24),
|
| 28 |
+
- # plus 2 points on the main diagonal y=x.
|
| 29 |
+
- # This avoids the grid-like local minimum and explores more complex structures.
|
| 30 |
+
+ # Initial placement: Hexagonal-like staggered grid (5-6-5-6-4 pattern)
|
| 31 |
+
+ # This pattern is known to be very efficient for 26 circles in a square.
|
| 32 |
+
count = 0
|
| 33 |
+
-
|
| 34 |
+
- # 1. Two points on the main diagonal
|
| 35 |
+
- diag_dist = 0.25
|
| 36 |
+
- centers[count] = [0.5 - diag_dist, 0.5 - diag_dist]; count += 1
|
| 37 |
+
- centers[count] = [0.5 + diag_dist, 0.5 + diag_dist]; count += 1
|
| 38 |
+
-
|
| 39 |
+
- # 2. Three seed points in the fundamental domain wedge (0.5 <= y <= x <= 1)
|
| 40 |
+
- seed_points = np.array([
|
| 41 |
+
- [0.8, 0.6],
|
| 42 |
+
- [0.9, 0.7],
|
| 43 |
+
- [0.7, 0.55]
|
| 44 |
+
- ])
|
| 45 |
+
-
|
| 46 |
+
- for p in seed_points:
|
| 47 |
+
- x, y = p[0], p[1]
|
| 48 |
+
- # Generate up to 8 points by D4 symmetry operations
|
| 49 |
+
- base_points = {(x, y), (y, x)}
|
| 50 |
+
- all_points = set()
|
| 51 |
+
- for pt in base_points:
|
| 52 |
+
- px, py = pt
|
| 53 |
+
- all_points.add((px, py))
|
| 54 |
+
- all_points.add((1 - px, py))
|
| 55 |
+
- all_points.add((px, 1 - py))
|
| 56 |
+
- all_points.add((1 - px, 1 - py))
|
| 57 |
+
-
|
| 58 |
+
- for point in sorted(list(all_points)): # sorted for determinism
|
| 59 |
+
- if count < n:
|
| 60 |
+
- centers[count] = point
|
| 61 |
+
- count += 1
|
| 62 |
+
-
|
| 63 |
+
- # Add small random perturbation to initial positions to break symmetry.
|
| 64 |
+
- # This helps the optimization escape local minima and explore more diverse configurations.
|
| 65 |
+
- centers += np.random.uniform(-0.02, 0.02, centers.shape)
|
| 66 |
+
-
|
| 67 |
+
- # Ensure initial centers are within the [0,1] bounds
|
| 68 |
+
+ num_rows = 5
|
| 69 |
+
+
|
| 70 |
+
+ # Y-coordinates for the 5 rows
|
| 71 |
+
+ # Distribute y-coordinates to be roughly centered in the row's vertical band
|
| 72 |
+
+ y_coords = np.linspace(0.1, 0.9, num_rows) # [0.1, 0.3, 0.5, 0.7, 0.9]
|
| 73 |
+
+
|
| 74 |
+
+ # X-coordinates for each type of row, designed for staggering
|
| 75 |
+
+ # These values are chosen to approximate a hexagonal packing structure.
|
| 76 |
+
+ x_coords_5_circles = np.linspace(0.1, 0.9, 5) # For rows with 5 circles
|
| 77 |
+
+ x_coords_6_circles = np.linspace(0.08, 0.92, 6) # For rows with 6 circles, slightly shifted for staggering
|
| 78 |
+
+ x_coords_4_circles = np.linspace(0.2, 0.8, 4) # For row with 4 circles, centered in gaps of 5-circle rows
|
| 79 |
+
+
|
| 80 |
+
+ # Place circles according to the 5-6-5-6-4 pattern
|
| 81 |
+
+ # Row 0: 5 circles (aligned)
|
| 82 |
+
+ for x_coord in x_coords_5_circles:
|
| 83 |
+
+ centers[count] = [x_coord, y_coords[0]]
|
| 84 |
+
+ count += 1
|
| 85 |
+
+
|
| 86 |
+
+ # Row 1: 6 circles (staggered)
|
| 87 |
+
+ for x_coord in x_coords_6_circles:
|
| 88 |
+
+ centers[count] = [x_coord, y_coords[1]]
|
| 89 |
+
+ count += 1
|
| 90 |
+
+
|
| 91 |
+
+ # Row 2: 5 circles (aligned)
|
| 92 |
+
+ for x_coord in x_coords_5_circles:
|
| 93 |
+
+ centers[count] = [x_coord, y_coords[2]]
|
| 94 |
+
+ count += 1
|
| 95 |
+
+
|
| 96 |
+
+ # Row 3: 6 circles (staggered)
|
| 97 |
+
+ for x_coord in x_coords_6_circles:
|
| 98 |
+
+ centers[count] = [x_coord, y_coords[3]]
|
| 99 |
+
+ count += 1
|
| 100 |
+
+
|
| 101 |
+
+ # Row 4: 4 circles (aligned to gaps of 5-circle rows)
|
| 102 |
+
+ for x_coord in x_coords_4_circles:
|
| 103 |
+
+ centers[count] = [x_coord, y_coords[4]]
|
| 104 |
+
+ count += 1
|
| 105 |
+
+
|
| 106 |
+
+ # Add small random perturbation to initial positions to break any perfect symmetry
|
| 107 |
+
+ # that might trap the simulation in a local minimum, allowing it to explore diverse configurations.
|
| 108 |
+
+ centers += np.random.uniform(-0.01, 0.01, centers.shape) # Smaller perturbation
|
| 109 |
+
+
|
| 110 |
+
+ # Ensure initial centers are within the [0,1] bounds. Small margin 1e-9
|
| 111 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 112 |
+
|
| 113 |
+
# --- Force-directed simulation parameters ---
|
| 114 |
+
- num_iterations_total = 4000 # Total number of simulation steps
|
| 115 |
+
+ num_iterations_total = 4000 # Total number of simulation steps (increased for better convergence)
|
| 116 |
+
initial_learning_rate = 0.01 # Initial step size for moving centers
|
| 117 |
+
- k_rep = 0.08 # Strength of circle-circle repulsion
|
| 118 |
+
- k_wall = 0.09 # Strength of wall repulsion
|
| 119 |
+
+ k_rep = 0.05 # Strength of circle-circle repulsion (tuned from previous successful programs)
|
| 120 |
+
+ k_wall = 0.1 # Strength of wall repulsion (tuned from previous successful programs)
|
| 121 |
+
|
| 122 |
+
# The learning rate decays over time, simulating an annealing process.
|
| 123 |
+
# This allows for larger movements initially and finer adjustments later.
|
| 124 |
+
current_learning_rate = initial_learning_rate
|
| 125 |
+
|
| 126 |
+
for iteration in range(num_iterations_total):
|
| 127 |
+
# 1. Compute radii for current centers.
|
| 128 |
+
# This function aims to maximize radii such that no circles overlap
|
| 129 |
+
# and all are within bounds for the given center positions.
|
| 130 |
+
radii = compute_max_radii_iterative(centers)
|
| 131 |
+
|
| 132 |
+
# 2. Calculate forces on centers.
|
| 133 |
+
# These forces push circles apart if they overlap or are too close to walls.
|
| 134 |
+
forces = np.zeros((n, 2))
|
| 135 |
+
|
| 136 |
+
for i in range(n):
|
| 137 |
+
# Wall repulsion: push circles away from boundaries if their radius extends beyond.
|
| 138 |
+
x, y = centers[i]
|
| 139 |
+
r = radii[i]
|
| 140 |
+
|
| 141 |
+
if x - r < 0: forces[i, 0] += k_wall * (r - x)
|
| 142 |
+
if x + r > 1: forces[i, 0] -= k_wall * ((x + r) - 1)
|
| 143 |
+
if y - r < 0: forces[i, 1] += k_wall * (r - y)
|
| 144 |
+
if y + r > 1: forces[i, 1] -= k_wall * ((y + r) - 1)
|
| 145 |
+
|
| 146 |
+
# Circle-circle repulsion: push overlapping circles apart.
|
| 147 |
+
for j in range(i + 1, n):
|
| 148 |
+
vec = centers[i] - centers[j]
|
| 149 |
+
dist = np.linalg.norm(vec)
|
| 150 |
+
|
| 151 |
+
min_separation = radii[i] + radii[j]
|
| 152 |
+
if dist < min_separation + 1e-9: # Overlap detected (with small tolerance)
|
| 153 |
+
overlap_amount = min_separation - dist
|
| 154 |
+
if dist > 1e-9: # Avoid division by zero if centers are too close
|
| 155 |
+
force_magnitude = k_rep * overlap_amount / dist
|
| 156 |
+
forces[i] += force_magnitude * vec
|
| 157 |
+
forces[j] -= force_magnitude * vec
|
| 158 |
+
else: # Centers are virtually identical, push in opposite random directions
|
| 159 |
+
random_vec = np.random.uniform(-1, 1, 2)
|
| 160 |
+
random_vec /= np.linalg.norm(random_vec)
|
| 161 |
+
forces[i] += k_rep * overlap_amount * random_vec
|
| 162 |
+
forces[j] -= k_rep * overlap_amount * random_vec
|
| 163 |
+
|
| 164 |
+
# 3. Update centers based on calculated forces.
|
| 165 |
+
centers += current_learning_rate * forces
|
| 166 |
+
|
| 167 |
+
# 4. Decay learning rate for annealing.
|
| 168 |
+
current_learning_rate = initial_learning_rate * (1 - iteration / num_iterations_total)
|
| 169 |
+
current_learning_rate = max(current_learning_rate, initial_learning_rate * 0.01) # Maintain a minimum step size
|
| 170 |
+
|
| 171 |
+
# 5. Clip centers to ensure they stay within the unit square.
|
| 172 |
+
# This is important for `compute_max_radii_iterative` to correctly calculate boundary limits.
|
| 173 |
+
centers = np.clip(centers, 1e-9, 1 - 1e-9)
|
| 174 |
+
|
| 175 |
+
# Final computation of radii after the simulation has settled.
|
| 176 |
+
final_radii = compute_max_radii_iterative(centers)
|
| 177 |
+
|
| 178 |
+
return centers, final_radii
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def compute_max_radii_iterative(centers):
|
| 182 |
+
"""
|
| 183 |
+
Computes the maximum possible radii for given circle centers such that all
|
| 184 |
+
circles are disjoint and contained within the unit square. This is an
|
| 185 |
+
iterative relaxation method that ensures convergence by repeatedly
|
| 186 |
+
checking and resolving overlaps.
|
| 187 |
+
|
| 188 |
+
Args:
|
| 189 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates of circle centers.
|
| 190 |
+
|
| 191 |
+
Returns:
|
| 192 |
+
np.array of shape (n) with the radius of each circle.
|
| 193 |
+
"""
|
| 194 |
+
n = centers.shape[0]
|
| 195 |
+
radii = np.zeros(n)
|
| 196 |
+
|
| 197 |
+
# Initialize radii based on the maximum possible distance to the nearest boundary.
|
| 198 |
+
# This ensures circles start as large as possible given only boundary constraints.
|
| 199 |
+
for i in range(n):
|
| 200 |
+
x, y = centers[i]
|
| 201 |
+
radii[i] = min(x, 1 - x, y, 1 - y)
|
| 202 |
+
radii[i] = max(radii[i], 1e-9) # Ensure a small positive radius to start.
|
| 203 |
+
|
| 204 |
+
# Iteratively adjust radii to prevent overlaps between circles.
|
| 205 |
+
# The loop runs for a fixed number of iterations to allow changes to propagate
|
| 206 |
+
# through the system until a stable state (or near-stable) is reached.
|
| 207 |
+
max_radii_solver_iterations = 200
|
| 208 |
+
|
| 209 |
+
for _ in range(max_radii_solver_iterations):
|
| 210 |
+
changed = False
|
| 211 |
+
for i in range(n):
|
| 212 |
+
# Re-check boundary conditions as radii might have been adjusted for overlaps.
|
| 213 |
+
x, y = centers[i]
|
| 214 |
+
boundary_limit = min(x, 1 - x, y, 1 - y)
|
| 215 |
+
if radii[i] > boundary_limit + 1e-9: # If radius exceeds boundary limit
|
| 216 |
+
radii[i] = boundary_limit
|
| 217 |
+
changed = True
|
| 218 |
+
|
| 219 |
+
# Check for overlaps with all other circles.
|
| 220 |
+
for j in range(n):
|
| 221 |
+
if i == j: continue
|
| 222 |
+
|
| 223 |
+
dist = np.linalg.norm(centers[i] - centers[j])
|
| 224 |
+
|
| 225 |
+
# If circles i and j overlap (with a small tolerance)
|
| 226 |
+
if radii[i] + radii[j] > dist + 1e-9:
|
| 227 |
+
sum_current_radii = radii[i] + radii[j]
|
| 228 |
+
if sum_current_radii > 1e-9: # Avoid division by zero
|
| 229 |
+
# Scale both radii proportionally so they just touch.
|
| 230 |
+
scale = dist / sum_current_radii
|
| 231 |
+
radii[i] *= scale
|
| 232 |
+
radii[j] *= scale
|
| 233 |
+
changed = True
|
| 234 |
+
else: # Both radii are near zero and centers are close. Assign tiny non-overlapping radii.
|
| 235 |
+
radii[i] = dist / 2.0 - 1e-10
|
| 236 |
+
radii[j] = dist / 2.0 - 1e-10
|
| 237 |
+
radii[i] = max(radii[i], 1e-9)
|
| 238 |
+
radii[j] = max(radii[j], 1e-9)
|
| 239 |
+
changed = True
|
| 240 |
+
if not changed:
|
| 241 |
+
break # If no radii were adjusted in an iteration, we've converged.
|
| 242 |
+
|
| 243 |
+
# Final pass to ensure all radii are non-negative and strictly within boundary limits.
|
| 244 |
+
for i in range(n):
|
| 245 |
+
x, y = centers[i]
|
| 246 |
+
radii[i] = max(0.0, radii[i]) # Radii must be non-negative.
|
| 247 |
+
radii[i] = min(radii[i], x, 1 - x, y, 1 - y) # Final clamp to boundaries.
|
| 248 |
+
|
| 249 |
+
return radii
|
| 250 |
+
# EVOLVE-BLOCK-END
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
# This part remains fixed (not evolved)
|
| 254 |
+
def run_packing():
|
| 255 |
+
"""Run the circle packing constructor for n=26"""
|
| 256 |
+
centers, radii = construct_packing()
|
| 257 |
+
# Calculate the sum of radii
|
| 258 |
+
sum_radii = np.sum(radii)
|
| 259 |
+
return centers, radii, sum_radii
|