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_gen200_period1000_20260206_165141/best/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/edit.diff +212 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/main.py +203 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/original.py +203 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/results/metrics.json +25 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/search_replace.txt +91 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_0/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_0/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_0/results/job_log.err +9 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_0/results/job_log.out +16 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_0/results/metrics.json +15 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_1/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_1/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_1/results/metrics.json +25 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/edit.diff +137 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/main.py +129 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/original.py +122 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/results/metrics.json +25 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/search_replace.txt +50 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_100/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_100/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_100/results/metrics.json +25 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_101/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_101/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_101/results/metrics.json +25 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/edit.diff +216 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/main.py +202 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/original.py +164 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/results/metrics.json +25 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/search_replace.txt +326 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_103/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_103/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_103/results/metrics.json +19 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/edit.diff +206 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/main.py +196 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/original.py +185 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/results/metrics.json +25 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/rewrite.txt +187 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_105/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_105/results/correct.json +4 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_105/results/metrics.json +19 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_106/__pycache__/main.cpython-313.pyc +0 -0
- examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_106/results/correct.json +4 -0
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (9.06 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/edit.diff
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--- a/original.py
|
| 2 |
+
+++ b/original.py
|
| 3 |
+
@@ -1,203 +1,203 @@
|
| 4 |
+
# EVOLVE-BLOCK-START
|
| 5 |
+
"""
|
| 6 |
+
Implements a long-run, highly adaptive Langevin-dynamics-inspired Simulated
|
| 7 |
+
Annealing (SA) algorithm for the circle packing problem. This hybrid version
|
| 8 |
+
combines the long search of one parent with the proven parameter set of a
|
| 9 |
+
higher-performing parent to achieve a superior result.
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import numpy as np
|
| 13 |
+
|
| 14 |
+
def _compute_forces(centers, radii, sensitivity, max_force):
|
| 15 |
+
"""Calculates repulsion forces based on gaps between circles and walls."""
|
| 16 |
+
n = centers.shape[0]
|
| 17 |
+
force_vectors = np.zeros_like(centers)
|
| 18 |
+
|
| 19 |
+
# Inter-circle forces
|
| 20 |
+
pdiff = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
|
| 21 |
+
pdist = np.sqrt(np.sum(pdiff**2, axis=-1))
|
| 22 |
+
np.fill_diagonal(pdist, np.inf)
|
| 23 |
+
|
| 24 |
+
radii_sum = radii[:, np.newaxis] + radii[np.newaxis, :]
|
| 25 |
+
gaps = pdist - radii_sum
|
| 26 |
+
force_magnitudes = np.exp(-sensitivity * gaps)
|
| 27 |
+
|
| 28 |
+
# Add a small epsilon to avoid division by zero
|
| 29 |
+
direction_vectors = pdiff / (pdist[..., np.newaxis] + 1e-12)
|
| 30 |
+
force_vectors += np.sum(force_magnitudes[..., np.newaxis] * direction_vectors, axis=1)
|
| 31 |
+
|
| 32 |
+
# Wall forces
|
| 33 |
+
gaps_walls = np.array([centers[:, 0] - radii, (1 - centers[:, 0]) - radii, centers[:, 1] - radii, (1 - centers[:, 1]) - radii]).T
|
| 34 |
+
force_magnitudes_walls = np.exp(-sensitivity * gaps_walls)
|
| 35 |
+
force_vectors[:, 0] += force_magnitudes_walls[:, 0]
|
| 36 |
+
force_vectors[:, 0] -= force_magnitudes_walls[:, 1]
|
| 37 |
+
force_vectors[:, 1] += force_magnitudes_walls[:, 2]
|
| 38 |
+
force_vectors[:, 1] -= force_magnitudes_walls[:, 3]
|
| 39 |
+
|
| 40 |
+
# Cap forces for stability
|
| 41 |
+
norms = np.linalg.norm(force_vectors, axis=1)
|
| 42 |
+
large_force_mask = norms > max_force
|
| 43 |
+
if np.any(large_force_mask):
|
| 44 |
+
force_vectors[large_force_mask] *= (max_force / (norms[large_force_mask, np.newaxis] + 1e-12))
|
| 45 |
+
|
| 46 |
+
return force_vectors
|
| 47 |
+
|
| 48 |
+
def construct_packing():
|
| 49 |
+
"""
|
| 50 |
+
Constructs an optimized arrangement of 26 circles using a hybrid
|
| 51 |
+
Langevin-dynamics-inspired Simulated Annealing (SA) algorithm.
|
| 52 |
+
"""
|
| 53 |
+
n = 26
|
| 54 |
+
|
| 55 |
+
# 1. Initial State: Proven 5x5 grid with two-stage perturbation.
|
| 56 |
+
centers = np.zeros((n, 2))
|
| 57 |
+
d = 0.09525
|
| 58 |
+
grid_coords = np.linspace(d, 1 - d, 5)
|
| 59 |
+
idx = 0
|
| 60 |
+
for x in grid_coords:
|
| 61 |
+
for y in grid_coords:
|
| 62 |
+
centers[idx] = [x, y]
|
| 63 |
+
idx += 1
|
| 64 |
+
centers[25] = [0.39, 0.41] # Key asymmetric perturbation
|
| 65 |
+
|
| 66 |
+
# Add a small random perturbation to break any remaining symmetries
|
| 67 |
+
perturbation_scale = 0.005
|
| 68 |
+
centers += np.random.uniform(-perturbation_scale, perturbation_scale, centers.shape)
|
| 69 |
+
centers = np.clip(centers, 0.01, 0.99)
|
| 70 |
+
|
| 71 |
+
# 2. Algorithm Parameters: Hybrid of long-run and proven parameters
|
| 72 |
+
max_steps = 150000
|
| 73 |
+
T_initial = 0.005
|
| 74 |
+
T_final = 1e-10 # Lower final temperature for the long run
|
| 75 |
+
alpha = (T_final / T_initial)**(1.0 / max_steps)
|
| 76 |
+
|
| 77 |
+
# Langevin Dynamics Parameters (from high-performing parent)
|
| 78 |
+
step_size_initial = 1.8e-3
|
| 79 |
+
noise_initial = 4.5e-3
|
| 80 |
+
|
| 81 |
+
# Force Calculation Parameter Schedules (from high-performing parent)
|
| 82 |
+
sensitivity_start = 180.0
|
| 83 |
+
- sensitivity_end = 250.0
|
| 84 |
+
+ sensitivity_end = 350.0 # Increased for sharper forces at end, closer to 2.61 ancestor (380.0)
|
| 85 |
+
max_force_start = 75.0
|
| 86 |
+
- max_force_end = 40.0
|
| 87 |
+
+ max_force_end = 30.0 # Reduced for finer control at end, closer to 2.61 ancestor (25.0)
|
| 88 |
+
|
| 89 |
+
# Radius Solver Parameter Schedules (long-run base with proven tweaks)
|
| 90 |
+
- q_iter_start, q_iter_end = 120, 500
|
| 91 |
+
+ q_iter_start, q_iter_end = 120, 400 # Aligned with 2.61 ancestor
|
| 92 |
+
q_init_uf_start, q_init_uf_end = 0.85, 0.6
|
| 93 |
+
q_final_uf_start, q_final_uf_end = 0.65, 0.4
|
| 94 |
+
q_switch_start, q_switch_end = 0.25, 0.5 # From high-performing parent
|
| 95 |
+
|
| 96 |
+
# 3. Initialization of State Variables and Schedules
|
| 97 |
+
current_centers = np.copy(centers)
|
| 98 |
+
best_centers = np.copy(centers)
|
| 99 |
+
|
| 100 |
+
# Pre-generate schedules for efficiency inside the loop
|
| 101 |
+
sensitivities = np.linspace(sensitivity_start, sensitivity_end, max_steps)
|
| 102 |
+
max_forces = np.linspace(max_force_start, max_force_end, max_steps)
|
| 103 |
+
q_iters = np.linspace(q_iter_start, q_iter_end, max_steps, dtype=int)
|
| 104 |
+
q_init_ufs = np.linspace(q_init_uf_start, q_init_uf_end, max_steps)
|
| 105 |
+
q_final_ufs = np.linspace(q_final_uf_start, q_final_uf_end, max_steps)
|
| 106 |
+
q_switches = np.linspace(q_switch_start, q_switch_end, max_steps)
|
| 107 |
+
|
| 108 |
+
# Initial evaluation
|
| 109 |
+
initial_solver_params = (q_init_ufs[0], q_final_ufs[0], q_switches[0])
|
| 110 |
+
current_radii = compute_max_radii(current_centers, iterations=q_iters[0], update_factor=initial_solver_params)
|
| 111 |
+
current_sum_radii = np.sum(current_radii)
|
| 112 |
+
best_sum_radii = current_sum_radii
|
| 113 |
+
|
| 114 |
+
T = T_initial
|
| 115 |
+
|
| 116 |
+
# 4. Main Langevin SA Loop
|
| 117 |
+
for step in range(max_steps):
|
| 118 |
+
# Anneal parameters based on temperature
|
| 119 |
+
anneal_factor = (T / T_initial)
|
| 120 |
+
step_sz = step_size_initial * anneal_factor**0.5
|
| 121 |
+
- noise_magnitude = noise_initial * anneal_factor
|
| 122 |
+
+ noise_magnitude = noise_initial * anneal_factor**0.5 # Aligned with 2.61 ancestor's scaling
|
| 123 |
+
|
| 124 |
+
# Get scheduled parameters for this step
|
| 125 |
+
sensitivity = sensitivities[step]
|
| 126 |
+
max_f = max_forces[step]
|
| 127 |
+
solver_params = (q_init_ufs[step], q_final_ufs[step], q_switches[step])
|
| 128 |
+
q_iter = q_iters[step]
|
| 129 |
+
|
| 130 |
+
# Propose a new state using Langevin dynamics
|
| 131 |
+
forces = _compute_forces(current_centers, current_radii, sensitivity, max_f)
|
| 132 |
+
noise = np.random.randn(n, 2) * noise_magnitude
|
| 133 |
+
move_vector = step_sz * forces + noise
|
| 134 |
+
|
| 135 |
+
candidate_centers = current_centers + move_vector
|
| 136 |
+
candidate_centers = np.clip(candidate_centers, 0.0, 1.0)
|
| 137 |
+
|
| 138 |
+
# Evaluate the new state
|
| 139 |
+
candidate_radii = compute_max_radii(candidate_centers, iterations=q_iter, update_factor=solver_params)
|
| 140 |
+
candidate_sum_radii = np.sum(candidate_radii)
|
| 141 |
+
|
| 142 |
+
# Metropolis-Hastings acceptance criterion (objective is to MAXIMIZE sum of radii)
|
| 143 |
+
delta_sum = candidate_sum_radii - current_sum_radii
|
| 144 |
+
if delta_sum > 0 or (T > 1e-12 and np.random.rand() < np.exp(delta_sum / T)):
|
| 145 |
+
current_centers = candidate_centers
|
| 146 |
+
current_sum_radii = candidate_sum_radii
|
| 147 |
+
current_radii = candidate_radii
|
| 148 |
+
|
| 149 |
+
# Update the best-ever found solution
|
| 150 |
+
if current_sum_radii > best_sum_radii:
|
| 151 |
+
best_sum_radii = current_sum_radii
|
| 152 |
+
best_centers = np.copy(current_centers)
|
| 153 |
+
|
| 154 |
+
# Cool down
|
| 155 |
+
T *= alpha
|
| 156 |
+
|
| 157 |
+
- # 5. Enhanced Final Polish (from high-performing parent)
|
| 158 |
+
- final_radii = compute_max_radii(best_centers, iterations=35000, update_factor=(0.7, 0.2, 0.5))
|
| 159 |
+
+ # 5. Enhanced Final Polish (from high-performing parent, adjusted to 2.61 ancestor)
|
| 160 |
+
+ final_radii = compute_max_radii(best_centers, iterations=35000, update_factor=(0.7, 0.3, 0.4))
|
| 161 |
+
|
| 162 |
+
return best_centers, final_radii
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
def compute_max_radii(centers, iterations=5000, update_factor=(0.6, 0.6, 0.0)):
|
| 166 |
+
"""
|
| 167 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 168 |
+
This version supports an adaptive damping schedule for the update factor.
|
| 169 |
+
"""
|
| 170 |
+
n = centers.shape[0]
|
| 171 |
+
radii = np.full(n, 1e-6)
|
| 172 |
+
|
| 173 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 174 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 175 |
+
|
| 176 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 177 |
+
|
| 178 |
+
if isinstance(update_factor, (float, int)):
|
| 179 |
+
initial_uf, final_uf, switch_ratio = float(update_factor), float(update_factor), 0.0
|
| 180 |
+
else:
|
| 181 |
+
initial_uf, final_uf, switch_ratio = update_factor
|
| 182 |
+
|
| 183 |
+
for k in range(iterations):
|
| 184 |
+
radii_old = np.copy(radii)
|
| 185 |
+
|
| 186 |
+
current_uf = initial_uf
|
| 187 |
+
if switch_ratio > 0 and k < iterations * switch_ratio:
|
| 188 |
+
progress = k / (iterations * switch_ratio)
|
| 189 |
+
current_uf = initial_uf + (final_uf - initial_uf) * progress
|
| 190 |
+
elif switch_ratio > 0:
|
| 191 |
+
current_uf = final_uf
|
| 192 |
+
|
| 193 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 194 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 195 |
+
new_r = np.maximum(0.0, potential_r)
|
| 196 |
+
|
| 197 |
+
radii = radii_old + current_uf * (new_r - radii_old)
|
| 198 |
+
|
| 199 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 200 |
+
break
|
| 201 |
+
|
| 202 |
+
return radii
|
| 203 |
+
# EVOLVE-BLOCK-END
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
# This part remains fixed (not evolved)
|
| 207 |
+
def run_packing():
|
| 208 |
+
"""Run the circle packing constructor for n=26"""
|
| 209 |
+
centers, radii = construct_packing()
|
| 210 |
+
# Calculate the sum of radii
|
| 211 |
+
sum_radii = np.sum(radii)
|
| 212 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/main.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
"""
|
| 3 |
+
Implements a long-run, highly adaptive Langevin-dynamics-inspired Simulated
|
| 4 |
+
Annealing (SA) algorithm for the circle packing problem. This hybrid version
|
| 5 |
+
combines the long search of one parent with the proven parameter set of a
|
| 6 |
+
higher-performing parent to achieve a superior result.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import numpy as np
|
| 10 |
+
|
| 11 |
+
def _compute_forces(centers, radii, sensitivity, max_force):
|
| 12 |
+
"""Calculates repulsion forces based on gaps between circles and walls."""
|
| 13 |
+
n = centers.shape[0]
|
| 14 |
+
force_vectors = np.zeros_like(centers)
|
| 15 |
+
|
| 16 |
+
# Inter-circle forces
|
| 17 |
+
pdiff = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
|
| 18 |
+
pdist = np.sqrt(np.sum(pdiff**2, axis=-1))
|
| 19 |
+
np.fill_diagonal(pdist, np.inf)
|
| 20 |
+
|
| 21 |
+
radii_sum = radii[:, np.newaxis] + radii[np.newaxis, :]
|
| 22 |
+
gaps = pdist - radii_sum
|
| 23 |
+
force_magnitudes = np.exp(-sensitivity * gaps)
|
| 24 |
+
|
| 25 |
+
# Add a small epsilon to avoid division by zero
|
| 26 |
+
direction_vectors = pdiff / (pdist[..., np.newaxis] + 1e-12)
|
| 27 |
+
force_vectors += np.sum(force_magnitudes[..., np.newaxis] * direction_vectors, axis=1)
|
| 28 |
+
|
| 29 |
+
# Wall forces
|
| 30 |
+
gaps_walls = np.array([centers[:, 0] - radii, (1 - centers[:, 0]) - radii, centers[:, 1] - radii, (1 - centers[:, 1]) - radii]).T
|
| 31 |
+
force_magnitudes_walls = np.exp(-sensitivity * gaps_walls)
|
| 32 |
+
force_vectors[:, 0] += force_magnitudes_walls[:, 0]
|
| 33 |
+
force_vectors[:, 0] -= force_magnitudes_walls[:, 1]
|
| 34 |
+
force_vectors[:, 1] += force_magnitudes_walls[:, 2]
|
| 35 |
+
force_vectors[:, 1] -= force_magnitudes_walls[:, 3]
|
| 36 |
+
|
| 37 |
+
# Cap forces for stability
|
| 38 |
+
norms = np.linalg.norm(force_vectors, axis=1)
|
| 39 |
+
large_force_mask = norms > max_force
|
| 40 |
+
if np.any(large_force_mask):
|
| 41 |
+
force_vectors[large_force_mask] *= (max_force / (norms[large_force_mask, np.newaxis] + 1e-12))
|
| 42 |
+
|
| 43 |
+
return force_vectors
|
| 44 |
+
|
| 45 |
+
def construct_packing():
|
| 46 |
+
"""
|
| 47 |
+
Constructs an optimized arrangement of 26 circles using a hybrid
|
| 48 |
+
Langevin-dynamics-inspired Simulated Annealing (SA) algorithm.
|
| 49 |
+
"""
|
| 50 |
+
n = 26
|
| 51 |
+
|
| 52 |
+
# 1. Initial State: Proven 5x5 grid with two-stage perturbation.
|
| 53 |
+
centers = np.zeros((n, 2))
|
| 54 |
+
d = 0.09525
|
| 55 |
+
grid_coords = np.linspace(d, 1 - d, 5)
|
| 56 |
+
idx = 0
|
| 57 |
+
for x in grid_coords:
|
| 58 |
+
for y in grid_coords:
|
| 59 |
+
centers[idx] = [x, y]
|
| 60 |
+
idx += 1
|
| 61 |
+
centers[25] = [0.39, 0.41] # Key asymmetric perturbation
|
| 62 |
+
|
| 63 |
+
# Add a small random perturbation to break any remaining symmetries
|
| 64 |
+
perturbation_scale = 0.005
|
| 65 |
+
centers += np.random.uniform(-perturbation_scale, perturbation_scale, centers.shape)
|
| 66 |
+
centers = np.clip(centers, 0.01, 0.99)
|
| 67 |
+
|
| 68 |
+
# 2. Algorithm Parameters: Hybrid of long-run and proven parameters
|
| 69 |
+
max_steps = 150000
|
| 70 |
+
T_initial = 0.005
|
| 71 |
+
T_final = 1e-10 # Lower final temperature for the long run
|
| 72 |
+
alpha = (T_final / T_initial)**(1.0 / max_steps)
|
| 73 |
+
|
| 74 |
+
# Langevin Dynamics Parameters (from high-performing parent)
|
| 75 |
+
step_size_initial = 1.8e-3
|
| 76 |
+
noise_initial = 4.5e-3
|
| 77 |
+
|
| 78 |
+
# Force Calculation Parameter Schedules (from high-performing parent)
|
| 79 |
+
sensitivity_start = 180.0
|
| 80 |
+
sensitivity_end = 350.0 # Increased for sharper forces at end, closer to 2.61 ancestor (380.0)
|
| 81 |
+
max_force_start = 75.0
|
| 82 |
+
max_force_end = 30.0 # Reduced for finer control at end, closer to 2.61 ancestor (25.0)
|
| 83 |
+
|
| 84 |
+
# Radius Solver Parameter Schedules (long-run base with proven tweaks)
|
| 85 |
+
q_iter_start, q_iter_end = 120, 400 # Aligned with 2.61 ancestor
|
| 86 |
+
q_init_uf_start, q_init_uf_end = 0.85, 0.6
|
| 87 |
+
q_final_uf_start, q_final_uf_end = 0.65, 0.4
|
| 88 |
+
q_switch_start, q_switch_end = 0.25, 0.5 # From high-performing parent
|
| 89 |
+
|
| 90 |
+
# 3. Initialization of State Variables and Schedules
|
| 91 |
+
current_centers = np.copy(centers)
|
| 92 |
+
best_centers = np.copy(centers)
|
| 93 |
+
|
| 94 |
+
# Pre-generate schedules for efficiency inside the loop
|
| 95 |
+
sensitivities = np.linspace(sensitivity_start, sensitivity_end, max_steps)
|
| 96 |
+
max_forces = np.linspace(max_force_start, max_force_end, max_steps)
|
| 97 |
+
q_iters = np.linspace(q_iter_start, q_iter_end, max_steps, dtype=int)
|
| 98 |
+
q_init_ufs = np.linspace(q_init_uf_start, q_init_uf_end, max_steps)
|
| 99 |
+
q_final_ufs = np.linspace(q_final_uf_start, q_final_uf_end, max_steps)
|
| 100 |
+
q_switches = np.linspace(q_switch_start, q_switch_end, max_steps)
|
| 101 |
+
|
| 102 |
+
# Initial evaluation
|
| 103 |
+
initial_solver_params = (q_init_ufs[0], q_final_ufs[0], q_switches[0])
|
| 104 |
+
current_radii = compute_max_radii(current_centers, iterations=q_iters[0], update_factor=initial_solver_params)
|
| 105 |
+
current_sum_radii = np.sum(current_radii)
|
| 106 |
+
best_sum_radii = current_sum_radii
|
| 107 |
+
|
| 108 |
+
T = T_initial
|
| 109 |
+
|
| 110 |
+
# 4. Main Langevin SA Loop
|
| 111 |
+
for step in range(max_steps):
|
| 112 |
+
# Anneal parameters based on temperature
|
| 113 |
+
anneal_factor = (T / T_initial)
|
| 114 |
+
step_sz = step_size_initial * anneal_factor**0.5
|
| 115 |
+
noise_magnitude = noise_initial * anneal_factor**0.5 # Aligned with 2.61 ancestor's scaling
|
| 116 |
+
|
| 117 |
+
# Get scheduled parameters for this step
|
| 118 |
+
sensitivity = sensitivities[step]
|
| 119 |
+
max_f = max_forces[step]
|
| 120 |
+
solver_params = (q_init_ufs[step], q_final_ufs[step], q_switches[step])
|
| 121 |
+
q_iter = q_iters[step]
|
| 122 |
+
|
| 123 |
+
# Propose a new state using Langevin dynamics
|
| 124 |
+
forces = _compute_forces(current_centers, current_radii, sensitivity, max_f)
|
| 125 |
+
noise = np.random.randn(n, 2) * noise_magnitude
|
| 126 |
+
move_vector = step_sz * forces + noise
|
| 127 |
+
|
| 128 |
+
candidate_centers = current_centers + move_vector
|
| 129 |
+
candidate_centers = np.clip(candidate_centers, 0.0, 1.0)
|
| 130 |
+
|
| 131 |
+
# Evaluate the new state
|
| 132 |
+
candidate_radii = compute_max_radii(candidate_centers, iterations=q_iter, update_factor=solver_params)
|
| 133 |
+
candidate_sum_radii = np.sum(candidate_radii)
|
| 134 |
+
|
| 135 |
+
# Metropolis-Hastings acceptance criterion (objective is to MAXIMIZE sum of radii)
|
| 136 |
+
delta_sum = candidate_sum_radii - current_sum_radii
|
| 137 |
+
if delta_sum > 0 or (T > 1e-12 and np.random.rand() < np.exp(delta_sum / T)):
|
| 138 |
+
current_centers = candidate_centers
|
| 139 |
+
current_sum_radii = candidate_sum_radii
|
| 140 |
+
current_radii = candidate_radii
|
| 141 |
+
|
| 142 |
+
# Update the best-ever found solution
|
| 143 |
+
if current_sum_radii > best_sum_radii:
|
| 144 |
+
best_sum_radii = current_sum_radii
|
| 145 |
+
best_centers = np.copy(current_centers)
|
| 146 |
+
|
| 147 |
+
# Cool down
|
| 148 |
+
T *= alpha
|
| 149 |
+
|
| 150 |
+
# 5. Enhanced Final Polish (from high-performing parent, adjusted to 2.61 ancestor)
|
| 151 |
+
final_radii = compute_max_radii(best_centers, iterations=35000, update_factor=(0.7, 0.3, 0.4))
|
| 152 |
+
|
| 153 |
+
return best_centers, final_radii
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def compute_max_radii(centers, iterations=5000, update_factor=(0.6, 0.6, 0.0)):
|
| 157 |
+
"""
|
| 158 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 159 |
+
This version supports an adaptive damping schedule for the update factor.
|
| 160 |
+
"""
|
| 161 |
+
n = centers.shape[0]
|
| 162 |
+
radii = np.full(n, 1e-6)
|
| 163 |
+
|
| 164 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 165 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 166 |
+
|
| 167 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 168 |
+
|
| 169 |
+
if isinstance(update_factor, (float, int)):
|
| 170 |
+
initial_uf, final_uf, switch_ratio = float(update_factor), float(update_factor), 0.0
|
| 171 |
+
else:
|
| 172 |
+
initial_uf, final_uf, switch_ratio = update_factor
|
| 173 |
+
|
| 174 |
+
for k in range(iterations):
|
| 175 |
+
radii_old = np.copy(radii)
|
| 176 |
+
|
| 177 |
+
current_uf = initial_uf
|
| 178 |
+
if switch_ratio > 0 and k < iterations * switch_ratio:
|
| 179 |
+
progress = k / (iterations * switch_ratio)
|
| 180 |
+
current_uf = initial_uf + (final_uf - initial_uf) * progress
|
| 181 |
+
elif switch_ratio > 0:
|
| 182 |
+
current_uf = final_uf
|
| 183 |
+
|
| 184 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 185 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 186 |
+
new_r = np.maximum(0.0, potential_r)
|
| 187 |
+
|
| 188 |
+
radii = radii_old + current_uf * (new_r - radii_old)
|
| 189 |
+
|
| 190 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 191 |
+
break
|
| 192 |
+
|
| 193 |
+
return radii
|
| 194 |
+
# EVOLVE-BLOCK-END
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
# This part remains fixed (not evolved)
|
| 198 |
+
def run_packing():
|
| 199 |
+
"""Run the circle packing constructor for n=26"""
|
| 200 |
+
centers, radii = construct_packing()
|
| 201 |
+
# Calculate the sum of radii
|
| 202 |
+
sum_radii = np.sum(radii)
|
| 203 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/original.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
"""
|
| 3 |
+
Implements a long-run, highly adaptive Langevin-dynamics-inspired Simulated
|
| 4 |
+
Annealing (SA) algorithm for the circle packing problem. This hybrid version
|
| 5 |
+
combines the long search of one parent with the proven parameter set of a
|
| 6 |
+
higher-performing parent to achieve a superior result.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import numpy as np
|
| 10 |
+
|
| 11 |
+
def _compute_forces(centers, radii, sensitivity, max_force):
|
| 12 |
+
"""Calculates repulsion forces based on gaps between circles and walls."""
|
| 13 |
+
n = centers.shape[0]
|
| 14 |
+
force_vectors = np.zeros_like(centers)
|
| 15 |
+
|
| 16 |
+
# Inter-circle forces
|
| 17 |
+
pdiff = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
|
| 18 |
+
pdist = np.sqrt(np.sum(pdiff**2, axis=-1))
|
| 19 |
+
np.fill_diagonal(pdist, np.inf)
|
| 20 |
+
|
| 21 |
+
radii_sum = radii[:, np.newaxis] + radii[np.newaxis, :]
|
| 22 |
+
gaps = pdist - radii_sum
|
| 23 |
+
force_magnitudes = np.exp(-sensitivity * gaps)
|
| 24 |
+
|
| 25 |
+
# Add a small epsilon to avoid division by zero
|
| 26 |
+
direction_vectors = pdiff / (pdist[..., np.newaxis] + 1e-12)
|
| 27 |
+
force_vectors += np.sum(force_magnitudes[..., np.newaxis] * direction_vectors, axis=1)
|
| 28 |
+
|
| 29 |
+
# Wall forces
|
| 30 |
+
gaps_walls = np.array([centers[:, 0] - radii, (1 - centers[:, 0]) - radii, centers[:, 1] - radii, (1 - centers[:, 1]) - radii]).T
|
| 31 |
+
force_magnitudes_walls = np.exp(-sensitivity * gaps_walls)
|
| 32 |
+
force_vectors[:, 0] += force_magnitudes_walls[:, 0]
|
| 33 |
+
force_vectors[:, 0] -= force_magnitudes_walls[:, 1]
|
| 34 |
+
force_vectors[:, 1] += force_magnitudes_walls[:, 2]
|
| 35 |
+
force_vectors[:, 1] -= force_magnitudes_walls[:, 3]
|
| 36 |
+
|
| 37 |
+
# Cap forces for stability
|
| 38 |
+
norms = np.linalg.norm(force_vectors, axis=1)
|
| 39 |
+
large_force_mask = norms > max_force
|
| 40 |
+
if np.any(large_force_mask):
|
| 41 |
+
force_vectors[large_force_mask] *= (max_force / (norms[large_force_mask, np.newaxis] + 1e-12))
|
| 42 |
+
|
| 43 |
+
return force_vectors
|
| 44 |
+
|
| 45 |
+
def construct_packing():
|
| 46 |
+
"""
|
| 47 |
+
Constructs an optimized arrangement of 26 circles using a hybrid
|
| 48 |
+
Langevin-dynamics-inspired Simulated Annealing (SA) algorithm.
|
| 49 |
+
"""
|
| 50 |
+
n = 26
|
| 51 |
+
|
| 52 |
+
# 1. Initial State: Proven 5x5 grid with two-stage perturbation.
|
| 53 |
+
centers = np.zeros((n, 2))
|
| 54 |
+
d = 0.09525
|
| 55 |
+
grid_coords = np.linspace(d, 1 - d, 5)
|
| 56 |
+
idx = 0
|
| 57 |
+
for x in grid_coords:
|
| 58 |
+
for y in grid_coords:
|
| 59 |
+
centers[idx] = [x, y]
|
| 60 |
+
idx += 1
|
| 61 |
+
centers[25] = [0.39, 0.41] # Key asymmetric perturbation
|
| 62 |
+
|
| 63 |
+
# Add a small random perturbation to break any remaining symmetries
|
| 64 |
+
perturbation_scale = 0.005
|
| 65 |
+
centers += np.random.uniform(-perturbation_scale, perturbation_scale, centers.shape)
|
| 66 |
+
centers = np.clip(centers, 0.01, 0.99)
|
| 67 |
+
|
| 68 |
+
# 2. Algorithm Parameters: Hybrid of long-run and proven parameters
|
| 69 |
+
max_steps = 150000
|
| 70 |
+
T_initial = 0.005
|
| 71 |
+
T_final = 1e-10 # Lower final temperature for the long run
|
| 72 |
+
alpha = (T_final / T_initial)**(1.0 / max_steps)
|
| 73 |
+
|
| 74 |
+
# Langevin Dynamics Parameters (from high-performing parent)
|
| 75 |
+
step_size_initial = 1.8e-3
|
| 76 |
+
noise_initial = 4.5e-3
|
| 77 |
+
|
| 78 |
+
# Force Calculation Parameter Schedules (from high-performing parent)
|
| 79 |
+
sensitivity_start = 180.0
|
| 80 |
+
sensitivity_end = 250.0
|
| 81 |
+
max_force_start = 75.0
|
| 82 |
+
max_force_end = 40.0
|
| 83 |
+
|
| 84 |
+
# Radius Solver Parameter Schedules (long-run base with proven tweaks)
|
| 85 |
+
q_iter_start, q_iter_end = 120, 500
|
| 86 |
+
q_init_uf_start, q_init_uf_end = 0.85, 0.6
|
| 87 |
+
q_final_uf_start, q_final_uf_end = 0.65, 0.4
|
| 88 |
+
q_switch_start, q_switch_end = 0.25, 0.5 # From high-performing parent
|
| 89 |
+
|
| 90 |
+
# 3. Initialization of State Variables and Schedules
|
| 91 |
+
current_centers = np.copy(centers)
|
| 92 |
+
best_centers = np.copy(centers)
|
| 93 |
+
|
| 94 |
+
# Pre-generate schedules for efficiency inside the loop
|
| 95 |
+
sensitivities = np.linspace(sensitivity_start, sensitivity_end, max_steps)
|
| 96 |
+
max_forces = np.linspace(max_force_start, max_force_end, max_steps)
|
| 97 |
+
q_iters = np.linspace(q_iter_start, q_iter_end, max_steps, dtype=int)
|
| 98 |
+
q_init_ufs = np.linspace(q_init_uf_start, q_init_uf_end, max_steps)
|
| 99 |
+
q_final_ufs = np.linspace(q_final_uf_start, q_final_uf_end, max_steps)
|
| 100 |
+
q_switches = np.linspace(q_switch_start, q_switch_end, max_steps)
|
| 101 |
+
|
| 102 |
+
# Initial evaluation
|
| 103 |
+
initial_solver_params = (q_init_ufs[0], q_final_ufs[0], q_switches[0])
|
| 104 |
+
current_radii = compute_max_radii(current_centers, iterations=q_iters[0], update_factor=initial_solver_params)
|
| 105 |
+
current_sum_radii = np.sum(current_radii)
|
| 106 |
+
best_sum_radii = current_sum_radii
|
| 107 |
+
|
| 108 |
+
T = T_initial
|
| 109 |
+
|
| 110 |
+
# 4. Main Langevin SA Loop
|
| 111 |
+
for step in range(max_steps):
|
| 112 |
+
# Anneal parameters based on temperature
|
| 113 |
+
anneal_factor = (T / T_initial)
|
| 114 |
+
step_sz = step_size_initial * anneal_factor**0.5
|
| 115 |
+
noise_magnitude = noise_initial * anneal_factor
|
| 116 |
+
|
| 117 |
+
# Get scheduled parameters for this step
|
| 118 |
+
sensitivity = sensitivities[step]
|
| 119 |
+
max_f = max_forces[step]
|
| 120 |
+
solver_params = (q_init_ufs[step], q_final_ufs[step], q_switches[step])
|
| 121 |
+
q_iter = q_iters[step]
|
| 122 |
+
|
| 123 |
+
# Propose a new state using Langevin dynamics
|
| 124 |
+
forces = _compute_forces(current_centers, current_radii, sensitivity, max_f)
|
| 125 |
+
noise = np.random.randn(n, 2) * noise_magnitude
|
| 126 |
+
move_vector = step_sz * forces + noise
|
| 127 |
+
|
| 128 |
+
candidate_centers = current_centers + move_vector
|
| 129 |
+
candidate_centers = np.clip(candidate_centers, 0.0, 1.0)
|
| 130 |
+
|
| 131 |
+
# Evaluate the new state
|
| 132 |
+
candidate_radii = compute_max_radii(candidate_centers, iterations=q_iter, update_factor=solver_params)
|
| 133 |
+
candidate_sum_radii = np.sum(candidate_radii)
|
| 134 |
+
|
| 135 |
+
# Metropolis-Hastings acceptance criterion (objective is to MAXIMIZE sum of radii)
|
| 136 |
+
delta_sum = candidate_sum_radii - current_sum_radii
|
| 137 |
+
if delta_sum > 0 or (T > 1e-12 and np.random.rand() < np.exp(delta_sum / T)):
|
| 138 |
+
current_centers = candidate_centers
|
| 139 |
+
current_sum_radii = candidate_sum_radii
|
| 140 |
+
current_radii = candidate_radii
|
| 141 |
+
|
| 142 |
+
# Update the best-ever found solution
|
| 143 |
+
if current_sum_radii > best_sum_radii:
|
| 144 |
+
best_sum_radii = current_sum_radii
|
| 145 |
+
best_centers = np.copy(current_centers)
|
| 146 |
+
|
| 147 |
+
# Cool down
|
| 148 |
+
T *= alpha
|
| 149 |
+
|
| 150 |
+
# 5. Enhanced Final Polish (from high-performing parent)
|
| 151 |
+
final_radii = compute_max_radii(best_centers, iterations=35000, update_factor=(0.7, 0.2, 0.5))
|
| 152 |
+
|
| 153 |
+
return best_centers, final_radii
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def compute_max_radii(centers, iterations=5000, update_factor=(0.6, 0.6, 0.0)):
|
| 157 |
+
"""
|
| 158 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 159 |
+
This version supports an adaptive damping schedule for the update factor.
|
| 160 |
+
"""
|
| 161 |
+
n = centers.shape[0]
|
| 162 |
+
radii = np.full(n, 1e-6)
|
| 163 |
+
|
| 164 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 165 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 166 |
+
|
| 167 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 168 |
+
|
| 169 |
+
if isinstance(update_factor, (float, int)):
|
| 170 |
+
initial_uf, final_uf, switch_ratio = float(update_factor), float(update_factor), 0.0
|
| 171 |
+
else:
|
| 172 |
+
initial_uf, final_uf, switch_ratio = update_factor
|
| 173 |
+
|
| 174 |
+
for k in range(iterations):
|
| 175 |
+
radii_old = np.copy(radii)
|
| 176 |
+
|
| 177 |
+
current_uf = initial_uf
|
| 178 |
+
if switch_ratio > 0 and k < iterations * switch_ratio:
|
| 179 |
+
progress = k / (iterations * switch_ratio)
|
| 180 |
+
current_uf = initial_uf + (final_uf - initial_uf) * progress
|
| 181 |
+
elif switch_ratio > 0:
|
| 182 |
+
current_uf = final_uf
|
| 183 |
+
|
| 184 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 185 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 186 |
+
new_r = np.maximum(0.0, potential_r)
|
| 187 |
+
|
| 188 |
+
radii = radii_old + current_uf * (new_r - radii_old)
|
| 189 |
+
|
| 190 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 191 |
+
break
|
| 192 |
+
|
| 193 |
+
return radii
|
| 194 |
+
# EVOLVE-BLOCK-END
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
# This part remains fixed (not evolved)
|
| 198 |
+
def run_packing():
|
| 199 |
+
"""Run the circle packing constructor for n=26"""
|
| 200 |
+
centers, radii = construct_packing()
|
| 201 |
+
# Calculate the sum of radii
|
| 202 |
+
sum_radii = np.sum(radii)
|
| 203 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/results/metrics.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 2.6094239779362547,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 2.6094239779362547,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.1147, 0.1147)\n centers[1] = (0.0675, 0.2907)\n centers[2] = (0.0957, 0.4517)\n centers[3] = (0.1064, 0.6536)\n centers[4] = (0.1208, 0.8798)\n centers[5] = (0.3267, 0.0977)\n centers[6] = (0.2560, 0.3052)\n centers[7] = (0.2765, 0.5326)\n centers[8] = (0.3055, 0.7418)\n centers[9] = (0.3134, 0.9254)\n centers[10] = (0.4838, 0.0628)\n centers[11] = (0.4842, 0.2406)\n centers[12] = (0.5673, 0.4119)\n centers[13] = (0.5011, 0.6054)\n centers[14] = (0.5143, 0.8672)\n centers[15] = (0.6418, 0.0966)\n centers[16] = (0.6983, 0.2892)\n centers[17] = (0.7229, 0.4907)\n centers[18] = (0.7347, 0.7201)\n centers[19] = (0.7164, 0.9251)\n centers[20] = (0.8700, 0.1299)\n centers[21] = (0.8942, 0.3643)\n centers[22] = (0.9033, 0.5665)\n centers[23] = (0.9329, 0.7276)\n centers[24] = (0.8949, 0.8952)\n centers[25] = (0.4168, 0.4187)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 2.6094239779362547
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 645.0352613227442,
|
| 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": 1770418650.434517,
|
| 24 |
+
"generation": 194
|
| 25 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/best/search_replace.txt
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<NAME>
|
| 2 |
+
align_langevin_params_fix
|
| 3 |
+
</NAME>
|
| 4 |
+
|
| 5 |
+
<DESCRIPTION>
|
| 6 |
+
This is a corrected attempt to align the Langevin dynamics and final polish parameters with a higher-performing ancestor. The previous attempt failed due to an indentation mismatch in the `SEARCH` block.
|
| 7 |
+
|
| 8 |
+
This edit precisely applies the following changes:
|
| 9 |
+
|
| 10 |
+
1. **Noise Magnitude Scaling:** Adjusts `noise_magnitude` to scale with `anneal_factor**0.5` instead of `anneal_factor`. This matches the successful scaling behavior observed in a higher-performing ancestor (score 2.61), providing a more balanced exploration/exploitation.
|
| 11 |
+
2. **Force Calculation & Radius Solver Parameters:** Updates `sensitivity_end`, `max_force_end`, and `q_iter_end` to tune the annealing process. `sensitivity_end` is increased for sharper forces at the end, `max_force_end` is reduced for finer control, and `q_iter_end` is slightly reduced to balance performance with the increased `max_steps`. These values are derived from successful ancestors.
|
| 12 |
+
3. **Final Polish Parameters:** Changes the `update_factor` for the final high-precision polish to `(0.7, 0.3, 0.4)`. This specific tuple for the update factor in `_compute_max_radii` was part of a robust final polish in a high-scoring prior program, and is expected to provide better local optimization.
|
| 13 |
+
</DESCRIPTION>
|
| 14 |
+
|
| 15 |
+
<DIFF>
|
| 16 |
+
<<<<<<< SEARCH
|
| 17 |
+
# Anneal parameters based on temperature
|
| 18 |
+
anneal_factor = (T / T_initial)
|
| 19 |
+
step_sz = step_size_initial * anneal_factor**0.5
|
| 20 |
+
noise_magnitude = noise_initial * anneal_factor
|
| 21 |
+
|
| 22 |
+
# Get scheduled parameters for this step
|
| 23 |
+
sensitivity = sensitivities[step]
|
| 24 |
+
max_f = max_forces[step]
|
| 25 |
+
solver_params = (q_init_ufs[step], q_final_ufs[step], q_switches[step])
|
| 26 |
+
q_iter = q_iters[step]
|
| 27 |
+
|
| 28 |
+
# Propose a new state using Langevin dynamics
|
| 29 |
+
forces = _compute_forces(current_centers, current_radii, sensitivity, max_f)
|
| 30 |
+
noise = np.random.randn(n, 2) * noise_magnitude
|
| 31 |
+
move_vector = step_sz * forces + noise
|
| 32 |
+
|
| 33 |
+
candidate_centers = current_centers + move_vector
|
| 34 |
+
candidate_centers = np.clip(candidate_centers, 0.0, 1.0)
|
| 35 |
+
=======
|
| 36 |
+
# Anneal parameters based on temperature
|
| 37 |
+
anneal_factor = (T / T_initial)
|
| 38 |
+
step_sz = step_size_initial * anneal_factor**0.5
|
| 39 |
+
noise_magnitude = noise_initial * anneal_factor**0.5 # Aligned with 2.61 ancestor's scaling
|
| 40 |
+
|
| 41 |
+
# Get scheduled parameters for this step
|
| 42 |
+
sensitivity = sensitivities[step]
|
| 43 |
+
max_f = max_forces[step]
|
| 44 |
+
solver_params = (q_init_ufs[step], q_final_ufs[step], q_switches[step])
|
| 45 |
+
q_iter = q_iters[step]
|
| 46 |
+
|
| 47 |
+
# Propose a new state using Langevin dynamics
|
| 48 |
+
forces = _compute_forces(current_centers, current_radii, sensitivity, max_f)
|
| 49 |
+
noise = np.random.randn(n, 2) * noise_magnitude
|
| 50 |
+
move_vector = step_sz * forces + noise
|
| 51 |
+
|
| 52 |
+
candidate_centers = current_centers + move_vector
|
| 53 |
+
candidate_centers = np.clip(candidate_centers, 0.0, 1.0)
|
| 54 |
+
>>>>>>> REPLACE
|
| 55 |
+
</DIFF>
|
| 56 |
+
|
| 57 |
+
<DIFF>
|
| 58 |
+
<<<<<<< SEARCH
|
| 59 |
+
sensitivity_start = 180.0
|
| 60 |
+
sensitivity_end = 250.0
|
| 61 |
+
max_force_start = 75.0
|
| 62 |
+
max_force_end = 40.0
|
| 63 |
+
|
| 64 |
+
# Radius Solver Parameter Schedules (long-run base with proven tweaks)
|
| 65 |
+
q_iter_start, q_iter_end = 120, 500
|
| 66 |
+
q_init_uf_start, q_init_uf_end = 0.85, 0.6
|
| 67 |
+
q_final_uf_start, q_final_uf_end = 0.65, 0.4
|
| 68 |
+
q_switch_start, q_switch_end = 0.25, 0.5 # From high-performing parent
|
| 69 |
+
=======
|
| 70 |
+
sensitivity_start = 180.0
|
| 71 |
+
sensitivity_end = 350.0 # Increased for sharper forces at end, closer to 2.61 ancestor (380.0)
|
| 72 |
+
max_force_start = 75.0
|
| 73 |
+
max_force_end = 30.0 # Reduced for finer control at end, closer to 2.61 ancestor (25.0)
|
| 74 |
+
|
| 75 |
+
# Radius Solver Parameter Schedules (long-run base with proven tweaks)
|
| 76 |
+
q_iter_start, q_iter_end = 120, 400 # Aligned with 2.61 ancestor
|
| 77 |
+
q_init_uf_start, q_init_uf_end = 0.85, 0.6
|
| 78 |
+
q_final_uf_start, q_final_uf_end = 0.65, 0.4
|
| 79 |
+
q_switch_start, q_switch_end = 0.25, 0.5 # From high-performing parent
|
| 80 |
+
>>>>>>> REPLACE
|
| 81 |
+
</DIFF>
|
| 82 |
+
|
| 83 |
+
<DIFF>
|
| 84 |
+
<<<<<<< SEARCH
|
| 85 |
+
# 5. Enhanced Final Polish (from high-performing parent)
|
| 86 |
+
final_radii = compute_max_radii(best_centers, iterations=35000, update_factor=(0.7, 0.2, 0.5))
|
| 87 |
+
=======
|
| 88 |
+
# 5. Enhanced Final Polish (from high-performing parent, adjusted to 2.61 ancestor)
|
| 89 |
+
final_radii = compute_max_radii(best_centers, iterations=35000, update_factor=(0.7, 0.3, 0.4))
|
| 90 |
+
>>>>>>> REPLACE
|
| 91 |
+
</DIFF>
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_0/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (3.15 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/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_gen200_period1000_20260206_165141/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_gen200_period1000_20260206_165141/gen_0/results/job_log.out
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Evaluating program: examples/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_0/main.py
|
| 2 |
+
Saving results to: examples/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_0/results
|
| 3 |
+
Run 1/1 completed in 0.00 seconds
|
| 4 |
+
Detailed packing data saved to examples/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_0/results/extra.npz
|
| 5 |
+
Correctness and error status saved to examples/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_0/results/correct.json
|
| 6 |
+
Metrics saved to examples/circle_packing/results/results_full_gen200_period1000_20260206_165141/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.0021205758675932884
|
| 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_gen200_period1000_20260206_165141/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.0021205758675932884,
|
| 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_gen200_period1000_20260206_165141/gen_1/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (2.76 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/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_gen200_period1000_20260206_165141/gen_1/results/metrics.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 1.9200929312704162,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 1.9200929312704162,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1000, 0.9000)\n centers[5] = (0.3000, 0.1000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.5000)\n centers[13] = (0.5000, 0.7000)\n centers[14] = (0.5000, 0.9000)\n centers[15] = (0.7000, 0.1000)\n centers[16] = (0.7000, 0.3000)\n centers[17] = (0.7000, 0.5000)\n centers[18] = (0.7000, 0.7000)\n centers[19] = (0.7000, 0.9000)\n centers[20] = (0.9000, 0.1000)\n centers[21] = (0.9000, 0.3000)\n centers[22] = (0.9000, 0.5000)\n centers[23] = (0.9000, 0.7000)\n centers[24] = (0.9000, 0.9000)\n centers[25] = (0.4000, 0.4000)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 1.9200929312704162
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 0.002143661491572857,
|
| 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": 1770396777.592588,
|
| 24 |
+
"generation": 1
|
| 25 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (3.96 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/edit.diff
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--- a/original.py
|
| 2 |
+
+++ b/original.py
|
| 3 |
+
@@ -1,122 +1,129 @@
|
| 4 |
+
# EVOLVE-BLOCK-START
|
| 5 |
+
"""Constructor-based circle packing for n=26 circles combining 5x5 grid layout
|
| 6 |
+
with iterative radius relaxation solver."""
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def construct_packing():
|
| 12 |
+
"""
|
| 13 |
+
Constructs an arrangement of 26 circles in a unit square based on a
|
| 14 |
+
5x5 grid pattern plus one extra circle, using an iterative radius
|
| 15 |
+
relaxation solver to maximize the sum of their radii.
|
| 16 |
+
|
| 17 |
+
Returns:
|
| 18 |
+
Tuple of (centers, radii)
|
| 19 |
+
centers: np.array of shape (26, 2) with (x, y) coordinates
|
| 20 |
+
radii: np.array of shape (26) with the radius of each circle
|
| 21 |
+
"""
|
| 22 |
+
n = 26
|
| 23 |
+
centers = np.zeros((n, 2))
|
| 24 |
+
|
| 25 |
+
# Place 25 circles in a 5x5 grid pattern.
|
| 26 |
+
# Centers are spaced such that they can initially have radius 0.1 each,
|
| 27 |
+
# fitting perfectly within the unit square if they all had r=0.1.
|
| 28 |
+
x_coords = np.linspace(0.1, 0.9, 5) # [0.1, 0.3, 0.5, 0.7, 0.9]
|
| 29 |
+
y_coords = np.linspace(0.1, 0.9, 5) # [0.1, 0.3, 0.5, 0.7, 0.9]
|
| 30 |
+
|
| 31 |
+
idx = 0
|
| 32 |
+
+ # Place 24 circles from the 5x5 grid, skipping the center point
|
| 33 |
+
for i in range(5):
|
| 34 |
+
for j in range(5):
|
| 35 |
+
+ # Use np.isclose for robust float comparison of the center point
|
| 36 |
+
+ if np.isclose(x_coords[i], 0.5) and np.isclose(y_coords[j], 0.5):
|
| 37 |
+
+ continue
|
| 38 |
+
centers[idx] = [x_coords[i], y_coords[j]]
|
| 39 |
+
idx += 1
|
| 40 |
+
|
| 41 |
+
- # Place the 26th circle in a different "hole" of the grid.
|
| 42 |
+
- # Instead of (0.4, 0.4), place it centrally along one of the vertical lines
|
| 43 |
+
- # of the grid, e.g., between (0.5, 0.1) and (0.5, 0.3). This might allow
|
| 44 |
+
- # for a larger radius for the 26th circle or better overall distribution.
|
| 45 |
+
- centers[idx] = [0.5, 0.2] # idx will be 25 here, for the 26th circle
|
| 46 |
+
+ # Place two circles symmetrically in the central hole created. This hole
|
| 47 |
+
+ # is larger than the standard grid holes. The original grid points
|
| 48 |
+
+ # at (0.3, 0.5) and (0.7, 0.5) are 0.4 apart. We place two circles
|
| 49 |
+
+ # in between. Placing them at 0.45 and 0.55 balances the space.
|
| 50 |
+
+ delta = 0.05
|
| 51 |
+
+ centers[idx] = [0.5 - delta, 0.5] # idx is 24
|
| 52 |
+
+ idx += 1
|
| 53 |
+
+ centers[idx] = [0.5 + delta, 0.5] # idx is 25
|
| 54 |
+
|
| 55 |
+
# Compute maximum valid radii for this configuration using the iterative solver.
|
| 56 |
+
radii = compute_max_radii(centers, iterations=200)
|
| 57 |
+
return centers, radii
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def compute_max_radii(centers, iterations=200):
|
| 61 |
+
"""
|
| 62 |
+
Computes maximum radii using an iterative relaxation method.
|
| 63 |
+
This method is robust for resolving overlaps and maintaining boundary constraints.
|
| 64 |
+
|
| 65 |
+
Args:
|
| 66 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates
|
| 67 |
+
iterations: The number of relaxation iterations to perform.
|
| 68 |
+
|
| 69 |
+
Returns:
|
| 70 |
+
np.array of shape (n) with the radius of each circle
|
| 71 |
+
"""
|
| 72 |
+
n = centers.shape[0]
|
| 73 |
+
radii = np.zeros(n)
|
| 74 |
+
|
| 75 |
+
# 1. Initialize radii based on wall distances
|
| 76 |
+
for i in range(n):
|
| 77 |
+
x, y = centers[i]
|
| 78 |
+
radii[i] = min(x, 1 - x, y, 1 - y)
|
| 79 |
+
|
| 80 |
+
# 2. Iteratively resolve overlaps
|
| 81 |
+
for _ in range(iterations):
|
| 82 |
+
max_change = 0.0
|
| 83 |
+
# Check for overlaps between pairs of circles
|
| 84 |
+
for i in range(n):
|
| 85 |
+
for j in range(i + 1, n):
|
| 86 |
+
dist = np.linalg.norm(centers[i] - centers[j])
|
| 87 |
+
|
| 88 |
+
# Handle extremely close or coincident centers gracefully
|
| 89 |
+
if dist < 1e-9:
|
| 90 |
+
radii[i] = 0.0
|
| 91 |
+
radii[j] = 0.0
|
| 92 |
+
max_change = float('inf') # Indicate a significant change occurred
|
| 93 |
+
continue
|
| 94 |
+
|
| 95 |
+
overlap = radii[i] + radii[j] - dist
|
| 96 |
+
if overlap > 0:
|
| 97 |
+
# Resolve overlap by shrinking radii proportionally to their current size.
|
| 98 |
+
total_radii_sum = radii[i] + radii[j]
|
| 99 |
+
|
| 100 |
+
# Handle case where total_radii_sum is near zero to avoid division by zero
|
| 101 |
+
if total_radii_sum < 1e-9:
|
| 102 |
+
radii[i] = 0.0
|
| 103 |
+
radii[j] = 0.0
|
| 104 |
+
continue
|
| 105 |
+
|
| 106 |
+
reduction_i = overlap * (radii[i] / total_radii_sum)
|
| 107 |
+
reduction_j = overlap * (radii[j] / total_radii_sum)
|
| 108 |
+
|
| 109 |
+
radii[i] -= reduction_i
|
| 110 |
+
radii[j] -= reduction_j
|
| 111 |
+
max_change = max(max_change, reduction_i, reduction_j)
|
| 112 |
+
|
| 113 |
+
# Enforce wall constraints after each pass.
|
| 114 |
+
# This is important as shrinking due to overlaps might allow expansion towards walls.
|
| 115 |
+
for i in range(n):
|
| 116 |
+
x, y = centers[i]
|
| 117 |
+
wall_rad = min(x, 1 - x, y, 1 - y)
|
| 118 |
+
if radii[i] > wall_rad:
|
| 119 |
+
change = radii[i] - wall_rad
|
| 120 |
+
radii[i] = wall_rad
|
| 121 |
+
max_change = max(max_change, change)
|
| 122 |
+
|
| 123 |
+
# If radii have stabilized (change is very small), we can exit early.
|
| 124 |
+
if max_change < 1e-7:
|
| 125 |
+
break
|
| 126 |
+
|
| 127 |
+
return radii
|
| 128 |
+
# EVOLVE-BLOCK-END
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
# This part remains fixed (not evolved)
|
| 132 |
+
def run_packing():
|
| 133 |
+
"""Run the circle packing constructor for n=26"""
|
| 134 |
+
centers, radii = construct_packing()
|
| 135 |
+
# Calculate the sum of radii
|
| 136 |
+
sum_radii = np.sum(radii)
|
| 137 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/main.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
"""Constructor-based circle packing for n=26 circles combining 5x5 grid layout
|
| 3 |
+
with iterative radius relaxation solver."""
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def construct_packing():
|
| 9 |
+
"""
|
| 10 |
+
Constructs an arrangement of 26 circles in a unit square based on a
|
| 11 |
+
5x5 grid pattern plus one extra circle, using an iterative radius
|
| 12 |
+
relaxation solver to maximize the sum of their radii.
|
| 13 |
+
|
| 14 |
+
Returns:
|
| 15 |
+
Tuple of (centers, radii)
|
| 16 |
+
centers: np.array of shape (26, 2) with (x, y) coordinates
|
| 17 |
+
radii: np.array of shape (26) with the radius of each circle
|
| 18 |
+
"""
|
| 19 |
+
n = 26
|
| 20 |
+
centers = np.zeros((n, 2))
|
| 21 |
+
|
| 22 |
+
# Place 25 circles in a 5x5 grid pattern.
|
| 23 |
+
# Centers are spaced such that they can initially have radius 0.1 each,
|
| 24 |
+
# fitting perfectly within the unit square if they all had r=0.1.
|
| 25 |
+
x_coords = np.linspace(0.1, 0.9, 5) # [0.1, 0.3, 0.5, 0.7, 0.9]
|
| 26 |
+
y_coords = np.linspace(0.1, 0.9, 5) # [0.1, 0.3, 0.5, 0.7, 0.9]
|
| 27 |
+
|
| 28 |
+
idx = 0
|
| 29 |
+
# Place 24 circles from the 5x5 grid, skipping the center point
|
| 30 |
+
for i in range(5):
|
| 31 |
+
for j in range(5):
|
| 32 |
+
# Use np.isclose for robust float comparison of the center point
|
| 33 |
+
if np.isclose(x_coords[i], 0.5) and np.isclose(y_coords[j], 0.5):
|
| 34 |
+
continue
|
| 35 |
+
centers[idx] = [x_coords[i], y_coords[j]]
|
| 36 |
+
idx += 1
|
| 37 |
+
|
| 38 |
+
# Place two circles symmetrically in the central hole created. This hole
|
| 39 |
+
# is larger than the standard grid holes. The original grid points
|
| 40 |
+
# at (0.3, 0.5) and (0.7, 0.5) are 0.4 apart. We place two circles
|
| 41 |
+
# in between. Placing them at 0.45 and 0.55 balances the space.
|
| 42 |
+
delta = 0.05
|
| 43 |
+
centers[idx] = [0.5 - delta, 0.5] # idx is 24
|
| 44 |
+
idx += 1
|
| 45 |
+
centers[idx] = [0.5 + delta, 0.5] # idx is 25
|
| 46 |
+
|
| 47 |
+
# Compute maximum valid radii for this configuration using the iterative solver.
|
| 48 |
+
radii = compute_max_radii(centers, iterations=200)
|
| 49 |
+
return centers, radii
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def compute_max_radii(centers, iterations=200):
|
| 53 |
+
"""
|
| 54 |
+
Computes maximum radii using an iterative relaxation method.
|
| 55 |
+
This method is robust for resolving overlaps and maintaining boundary constraints.
|
| 56 |
+
|
| 57 |
+
Args:
|
| 58 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates
|
| 59 |
+
iterations: The number of relaxation iterations to perform.
|
| 60 |
+
|
| 61 |
+
Returns:
|
| 62 |
+
np.array of shape (n) with the radius of each circle
|
| 63 |
+
"""
|
| 64 |
+
n = centers.shape[0]
|
| 65 |
+
radii = np.zeros(n)
|
| 66 |
+
|
| 67 |
+
# 1. Initialize radii based on wall distances
|
| 68 |
+
for i in range(n):
|
| 69 |
+
x, y = centers[i]
|
| 70 |
+
radii[i] = min(x, 1 - x, y, 1 - y)
|
| 71 |
+
|
| 72 |
+
# 2. Iteratively resolve overlaps
|
| 73 |
+
for _ in range(iterations):
|
| 74 |
+
max_change = 0.0
|
| 75 |
+
# Check for overlaps between pairs of circles
|
| 76 |
+
for i in range(n):
|
| 77 |
+
for j in range(i + 1, n):
|
| 78 |
+
dist = np.linalg.norm(centers[i] - centers[j])
|
| 79 |
+
|
| 80 |
+
# Handle extremely close or coincident centers gracefully
|
| 81 |
+
if dist < 1e-9:
|
| 82 |
+
radii[i] = 0.0
|
| 83 |
+
radii[j] = 0.0
|
| 84 |
+
max_change = float('inf') # Indicate a significant change occurred
|
| 85 |
+
continue
|
| 86 |
+
|
| 87 |
+
overlap = radii[i] + radii[j] - dist
|
| 88 |
+
if overlap > 0:
|
| 89 |
+
# Resolve overlap by shrinking radii proportionally to their current size.
|
| 90 |
+
total_radii_sum = radii[i] + radii[j]
|
| 91 |
+
|
| 92 |
+
# Handle case where total_radii_sum is near zero to avoid division by zero
|
| 93 |
+
if total_radii_sum < 1e-9:
|
| 94 |
+
radii[i] = 0.0
|
| 95 |
+
radii[j] = 0.0
|
| 96 |
+
continue
|
| 97 |
+
|
| 98 |
+
reduction_i = overlap * (radii[i] / total_radii_sum)
|
| 99 |
+
reduction_j = overlap * (radii[j] / total_radii_sum)
|
| 100 |
+
|
| 101 |
+
radii[i] -= reduction_i
|
| 102 |
+
radii[j] -= reduction_j
|
| 103 |
+
max_change = max(max_change, reduction_i, reduction_j)
|
| 104 |
+
|
| 105 |
+
# Enforce wall constraints after each pass.
|
| 106 |
+
# This is important as shrinking due to overlaps might allow expansion towards walls.
|
| 107 |
+
for i in range(n):
|
| 108 |
+
x, y = centers[i]
|
| 109 |
+
wall_rad = min(x, 1 - x, y, 1 - y)
|
| 110 |
+
if radii[i] > wall_rad:
|
| 111 |
+
change = radii[i] - wall_rad
|
| 112 |
+
radii[i] = wall_rad
|
| 113 |
+
max_change = max(max_change, change)
|
| 114 |
+
|
| 115 |
+
# If radii have stabilized (change is very small), we can exit early.
|
| 116 |
+
if max_change < 1e-7:
|
| 117 |
+
break
|
| 118 |
+
|
| 119 |
+
return radii
|
| 120 |
+
# EVOLVE-BLOCK-END
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
# This part remains fixed (not evolved)
|
| 124 |
+
def run_packing():
|
| 125 |
+
"""Run the circle packing constructor for n=26"""
|
| 126 |
+
centers, radii = construct_packing()
|
| 127 |
+
# Calculate the sum of radii
|
| 128 |
+
sum_radii = np.sum(radii)
|
| 129 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/original.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
"""Constructor-based circle packing for n=26 circles combining 5x5 grid layout
|
| 3 |
+
with iterative radius relaxation solver."""
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def construct_packing():
|
| 9 |
+
"""
|
| 10 |
+
Constructs an arrangement of 26 circles in a unit square based on a
|
| 11 |
+
5x5 grid pattern plus one extra circle, using an iterative radius
|
| 12 |
+
relaxation solver to maximize the sum of their radii.
|
| 13 |
+
|
| 14 |
+
Returns:
|
| 15 |
+
Tuple of (centers, radii)
|
| 16 |
+
centers: np.array of shape (26, 2) with (x, y) coordinates
|
| 17 |
+
radii: np.array of shape (26) with the radius of each circle
|
| 18 |
+
"""
|
| 19 |
+
n = 26
|
| 20 |
+
centers = np.zeros((n, 2))
|
| 21 |
+
|
| 22 |
+
# Place 25 circles in a 5x5 grid pattern.
|
| 23 |
+
# Centers are spaced such that they can initially have radius 0.1 each,
|
| 24 |
+
# fitting perfectly within the unit square if they all had r=0.1.
|
| 25 |
+
x_coords = np.linspace(0.1, 0.9, 5) # [0.1, 0.3, 0.5, 0.7, 0.9]
|
| 26 |
+
y_coords = np.linspace(0.1, 0.9, 5) # [0.1, 0.3, 0.5, 0.7, 0.9]
|
| 27 |
+
|
| 28 |
+
idx = 0
|
| 29 |
+
for i in range(5):
|
| 30 |
+
for j in range(5):
|
| 31 |
+
centers[idx] = [x_coords[i], y_coords[j]]
|
| 32 |
+
idx += 1
|
| 33 |
+
|
| 34 |
+
# Place the 26th circle in a different "hole" of the grid.
|
| 35 |
+
# Instead of (0.4, 0.4), place it centrally along one of the vertical lines
|
| 36 |
+
# of the grid, e.g., between (0.5, 0.1) and (0.5, 0.3). This might allow
|
| 37 |
+
# for a larger radius for the 26th circle or better overall distribution.
|
| 38 |
+
centers[idx] = [0.5, 0.2] # idx will be 25 here, for the 26th circle
|
| 39 |
+
|
| 40 |
+
# Compute maximum valid radii for this configuration using the iterative solver.
|
| 41 |
+
radii = compute_max_radii(centers, iterations=200)
|
| 42 |
+
return centers, radii
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def compute_max_radii(centers, iterations=200):
|
| 46 |
+
"""
|
| 47 |
+
Computes maximum radii using an iterative relaxation method.
|
| 48 |
+
This method is robust for resolving overlaps and maintaining boundary constraints.
|
| 49 |
+
|
| 50 |
+
Args:
|
| 51 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates
|
| 52 |
+
iterations: The number of relaxation iterations to perform.
|
| 53 |
+
|
| 54 |
+
Returns:
|
| 55 |
+
np.array of shape (n) with the radius of each circle
|
| 56 |
+
"""
|
| 57 |
+
n = centers.shape[0]
|
| 58 |
+
radii = np.zeros(n)
|
| 59 |
+
|
| 60 |
+
# 1. Initialize radii based on wall distances
|
| 61 |
+
for i in range(n):
|
| 62 |
+
x, y = centers[i]
|
| 63 |
+
radii[i] = min(x, 1 - x, y, 1 - y)
|
| 64 |
+
|
| 65 |
+
# 2. Iteratively resolve overlaps
|
| 66 |
+
for _ in range(iterations):
|
| 67 |
+
max_change = 0.0
|
| 68 |
+
# Check for overlaps between pairs of circles
|
| 69 |
+
for i in range(n):
|
| 70 |
+
for j in range(i + 1, n):
|
| 71 |
+
dist = np.linalg.norm(centers[i] - centers[j])
|
| 72 |
+
|
| 73 |
+
# Handle extremely close or coincident centers gracefully
|
| 74 |
+
if dist < 1e-9:
|
| 75 |
+
radii[i] = 0.0
|
| 76 |
+
radii[j] = 0.0
|
| 77 |
+
max_change = float('inf') # Indicate a significant change occurred
|
| 78 |
+
continue
|
| 79 |
+
|
| 80 |
+
overlap = radii[i] + radii[j] - dist
|
| 81 |
+
if overlap > 0:
|
| 82 |
+
# Resolve overlap by shrinking radii proportionally to their current size.
|
| 83 |
+
total_radii_sum = radii[i] + radii[j]
|
| 84 |
+
|
| 85 |
+
# Handle case where total_radii_sum is near zero to avoid division by zero
|
| 86 |
+
if total_radii_sum < 1e-9:
|
| 87 |
+
radii[i] = 0.0
|
| 88 |
+
radii[j] = 0.0
|
| 89 |
+
continue
|
| 90 |
+
|
| 91 |
+
reduction_i = overlap * (radii[i] / total_radii_sum)
|
| 92 |
+
reduction_j = overlap * (radii[j] / total_radii_sum)
|
| 93 |
+
|
| 94 |
+
radii[i] -= reduction_i
|
| 95 |
+
radii[j] -= reduction_j
|
| 96 |
+
max_change = max(max_change, reduction_i, reduction_j)
|
| 97 |
+
|
| 98 |
+
# Enforce wall constraints after each pass.
|
| 99 |
+
# This is important as shrinking due to overlaps might allow expansion towards walls.
|
| 100 |
+
for i in range(n):
|
| 101 |
+
x, y = centers[i]
|
| 102 |
+
wall_rad = min(x, 1 - x, y, 1 - y)
|
| 103 |
+
if radii[i] > wall_rad:
|
| 104 |
+
change = radii[i] - wall_rad
|
| 105 |
+
radii[i] = wall_rad
|
| 106 |
+
max_change = max(max_change, change)
|
| 107 |
+
|
| 108 |
+
# If radii have stabilized (change is very small), we can exit early.
|
| 109 |
+
if max_change < 1e-7:
|
| 110 |
+
break
|
| 111 |
+
|
| 112 |
+
return radii
|
| 113 |
+
# EVOLVE-BLOCK-END
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
# This part remains fixed (not evolved)
|
| 117 |
+
def run_packing():
|
| 118 |
+
"""Run the circle packing constructor for n=26"""
|
| 119 |
+
centers, radii = construct_packing()
|
| 120 |
+
# Calculate the sum of radii
|
| 121 |
+
sum_radii = np.sum(radii)
|
| 122 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/results/metrics.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 1.826354003799491,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 1.826354003799491,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.1000, 0.1000)\n centers[1] = (0.1000, 0.3000)\n centers[2] = (0.1000, 0.5000)\n centers[3] = (0.1000, 0.7000)\n centers[4] = (0.1000, 0.9000)\n centers[5] = (0.3000, 0.1000)\n centers[6] = (0.3000, 0.3000)\n centers[7] = (0.3000, 0.5000)\n centers[8] = (0.3000, 0.7000)\n centers[9] = (0.3000, 0.9000)\n centers[10] = (0.5000, 0.1000)\n centers[11] = (0.5000, 0.3000)\n centers[12] = (0.5000, 0.7000)\n centers[13] = (0.5000, 0.9000)\n centers[14] = (0.7000, 0.1000)\n centers[15] = (0.7000, 0.3000)\n centers[16] = (0.7000, 0.5000)\n centers[17] = (0.7000, 0.7000)\n centers[18] = (0.7000, 0.9000)\n centers[19] = (0.9000, 0.1000)\n centers[20] = (0.9000, 0.3000)\n centers[21] = (0.9000, 0.5000)\n centers[22] = (0.9000, 0.7000)\n centers[23] = (0.9000, 0.9000)\n centers[24] = (0.4500, 0.5000)\n centers[25] = (0.5500, 0.5000)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 1.826354003799491
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 0.003152531571686268,
|
| 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": 1770397837.0687263,
|
| 24 |
+
"generation": 10
|
| 25 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_10/search_replace.txt
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<NAME>
|
| 2 |
+
replace_center_with_dimer
|
| 3 |
+
</NAME>
|
| 4 |
+
|
| 5 |
+
<DESCRIPTION>
|
| 6 |
+
The existing best-performing solutions are based on a 5x5 grid with an additional circle placed in one of the grid 'holes'. This suggests the grid structure is promising but needs modification for N=26. The current implementation scores lower (1.88) than the best-seen score (1.92) by placing the 26th circle in a suboptimal position.
|
| 7 |
+
|
| 8 |
+
Instead of simply reverting to the previous best position `(0.4, 0.4)`, this edit proposes a more fundamental and geometrically-motivated change. I will modify the `construct_packing` function to create a known "defect" structure often seen in sphere packing, which might yield a better overall sum of radii.
|
| 9 |
+
|
| 10 |
+
The change is as follows:
|
| 11 |
+
1. The 5x5 grid is created, but the centermost circle at `(0.5, 0.5)` is omitted. This creates a larger central empty space (a 'vacancy') compared to the standard grid holes.
|
| 12 |
+
2. Into this larger vacancy, two circles are placed symmetrically instead of one. The horizontal space between grid points `(0.3, 0.5)` and `(0.7, 0.5)` is `0.4`. Placing two circles in this gap requires careful positioning. I've chosen to place them at `(0.45, 0.5)` and `(0.55, 0.5)`. This balances the distance between the two new circles (`0.1`) and their distance to the nearest grid neighbors (`0.15`).
|
| 13 |
+
|
| 14 |
+
This "dimer-in-vacancy" approach breaks the perfect grid symmetry in a controlled way, which can lead to more efficient packing and is a common strategy for accommodating non-matching numbers in lattice-based packing. I also made the floating-point comparison more robust by using `np.isclose`.
|
| 15 |
+
</DESCRIPTION>
|
| 16 |
+
|
| 17 |
+
<DIFF>
|
| 18 |
+
<<<<<<< SEARCH
|
| 19 |
+
idx = 0
|
| 20 |
+
for i in range(5):
|
| 21 |
+
for j in range(5):
|
| 22 |
+
centers[idx] = [x_coords[i], y_coords[j]]
|
| 23 |
+
idx += 1
|
| 24 |
+
|
| 25 |
+
# Place the 26th circle in a different "hole" of the grid.
|
| 26 |
+
# Instead of (0.4, 0.4), place it centrally along one of the vertical lines
|
| 27 |
+
# of the grid, e.g., between (0.5, 0.1) and (0.5, 0.3). This might allow
|
| 28 |
+
# for a larger radius for the 26th circle or better overall distribution.
|
| 29 |
+
centers[idx] = [0.5, 0.2] # idx will be 25 here, for the 26th circle
|
| 30 |
+
=======
|
| 31 |
+
idx = 0
|
| 32 |
+
# Place 24 circles from the 5x5 grid, skipping the center point
|
| 33 |
+
for i in range(5):
|
| 34 |
+
for j in range(5):
|
| 35 |
+
# Use np.isclose for robust float comparison of the center point
|
| 36 |
+
if np.isclose(x_coords[i], 0.5) and np.isclose(y_coords[j], 0.5):
|
| 37 |
+
continue
|
| 38 |
+
centers[idx] = [x_coords[i], y_coords[j]]
|
| 39 |
+
idx += 1
|
| 40 |
+
|
| 41 |
+
# Place two circles symmetrically in the central hole created. This hole
|
| 42 |
+
# is larger than the standard grid holes. The original grid points
|
| 43 |
+
# at (0.3, 0.5) and (0.7, 0.5) are 0.4 apart. We place two circles
|
| 44 |
+
# in between. Placing them at 0.45 and 0.55 balances the space.
|
| 45 |
+
delta = 0.05
|
| 46 |
+
centers[idx] = [0.5 - delta, 0.5] # idx is 24
|
| 47 |
+
idx += 1
|
| 48 |
+
centers[idx] = [0.5 + delta, 0.5] # idx is 25
|
| 49 |
+
>>>>>>> REPLACE
|
| 50 |
+
</DIFF>
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_100/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (6.57 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_100/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_100/results/metrics.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 2.535779246674928,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 2.535779246674928,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.1097, 0.1087)\n centers[1] = (0.0811, 0.2964)\n centers[2] = (0.1174, 0.4812)\n centers[3] = (0.1123, 0.7007)\n centers[4] = (0.0939, 0.9061)\n centers[5] = (0.2985, 0.0817)\n centers[6] = (0.2546, 0.2550)\n centers[7] = (0.2926, 0.5329)\n centers[8] = (0.3008, 0.6849)\n centers[9] = (0.3039, 0.8795)\n centers[10] = (0.4734, 0.0936)\n centers[11] = (0.5042, 0.2915)\n centers[12] = (0.4926, 0.4887)\n centers[13] = (0.4950, 0.6974)\n centers[14] = (0.5125, 0.9072)\n centers[15] = (0.6730, 0.1065)\n centers[16] = (0.7122, 0.3116)\n centers[17] = (0.6804, 0.5095)\n centers[18] = (0.7152, 0.7072)\n centers[19] = (0.7007, 0.9046)\n centers[20] = (0.8853, 0.1058)\n centers[21] = (0.9076, 0.3027)\n centers[22] = (0.8889, 0.5047)\n centers[23] = (0.9089, 0.7052)\n centers[24] = (0.8980, 0.8980)\n centers[25] = (0.3548, 0.3955)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 2.535779246674928
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 47.51581493206322,
|
| 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": 1770405446.6050518,
|
| 24 |
+
"generation": 100
|
| 25 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_101/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (6.52 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_101/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_101/results/metrics.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 2.5307582777428874,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 2.5307582777428874,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.1014, 0.1013)\n centers[1] = (0.0949, 0.2973)\n centers[2] = (0.1231, 0.5127)\n centers[3] = (0.0859, 0.7176)\n centers[4] = (0.0986, 0.9015)\n centers[5] = (0.3038, 0.1011)\n centers[6] = (0.2877, 0.2994)\n centers[7] = (0.3098, 0.5117)\n centers[8] = (0.2813, 0.6854)\n centers[9] = (0.2989, 0.8982)\n centers[10] = (0.5091, 0.1035)\n centers[11] = (0.5086, 0.3027)\n centers[12] = (0.5253, 0.5202)\n centers[13] = (0.4924, 0.7186)\n centers[14] = (0.4901, 0.9103)\n centers[15] = (0.7160, 0.1018)\n centers[16] = (0.6947, 0.2928)\n centers[17] = (0.7090, 0.4733)\n centers[18] = (0.7013, 0.6786)\n centers[19] = (0.6833, 0.8959)\n centers[20] = (0.9087, 0.0912)\n centers[21] = (0.8924, 0.2890)\n centers[22] = (0.8990, 0.4972)\n centers[23] = (0.9058, 0.6923)\n centers[24] = (0.8936, 0.8926)\n centers[25] = (0.3978, 0.4182)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 2.5307582777428874
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 60.76680243201554,
|
| 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": 1770405497.4442744,
|
| 24 |
+
"generation": 101
|
| 25 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (7.53 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/edit.diff
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--- a/original.py
|
| 2 |
+
+++ b/original.py
|
| 3 |
+
@@ -1,164 +1,202 @@
|
| 4 |
+
# EVOLVE-BLOCK-START
|
| 5 |
+
"""Constructor-based circle packing for n=26 circles combining 5x5 grid layout
|
| 6 |
+
with iterative radius relaxation solver."""
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def construct_packing():
|
| 12 |
+
"""
|
| 13 |
+
Constructs an optimized arrangement of 26 circles by starting with a strong
|
| 14 |
+
grid-based pattern and refining center positions using a coordinate ascent
|
| 15 |
+
method with a rich neighborhood and dynamic solver parameters.
|
| 16 |
+
|
| 17 |
+
Returns:
|
| 18 |
+
A tuple containing:
|
| 19 |
+
centers: np.array of shape (26, 2) with the final optimized (x, y) coordinates
|
| 20 |
+
radii: np.array of shape (26) with the final radius of each circle
|
| 21 |
+
"""
|
| 22 |
+
n = 26
|
| 23 |
+
centers = np.zeros((n, 2))
|
| 24 |
+
|
| 25 |
+
# 1. Initial Placement: Start with a 5x5 grid + central circle pattern.
|
| 26 |
+
# Adopt the optimized margin 'd' from a successful prior for a potentially better
|
| 27 |
+
# initial grid configuration. This value is known to be effective for dense packings.
|
| 28 |
+
d = 0.09525
|
| 29 |
+
grid_coords = np.linspace(d, 1 - d, 5)
|
| 30 |
+
idx = 0
|
| 31 |
+
for x in grid_coords:
|
| 32 |
+
for y in grid_coords:
|
| 33 |
+
centers[idx] = [x, y]
|
| 34 |
+
idx += 1
|
| 35 |
+
|
| 36 |
+
# Perturb the 26th circle to break initial symmetry. This is critical for escaping local optima.
|
| 37 |
+
# Revert to the fixed, proven value instead of a random one for a stable, high-quality start.
|
| 38 |
+
centers[25] = [0.39, 0.41]
|
| 39 |
+
|
| 40 |
+
+ # Apply small random perturbations to all centers to help escape local optima
|
| 41 |
+
+ # by introducing slight initial asymmetry and exploring varied starting points.
|
| 42 |
+
+ # These scales are based on successful prior implementations.
|
| 43 |
+
+ perturbation_scale_grid = 0.005 # For the 5x5 grid circles
|
| 44 |
+
+ perturbation_scale_26th = 0.01 # For the 26th central circle
|
| 45 |
+
+
|
| 46 |
+
+ centers[:25, 0] += np.random.uniform(-perturbation_scale_grid, perturbation_scale_grid, 25)
|
| 47 |
+
+ centers[:25, 1] += np.random.uniform(-perturbation_scale_grid, perturbation_scale_grid, 25)
|
| 48 |
+
+ centers[25, 0] += np.random.uniform(-perturbation_scale_26th, perturbation_scale_26th)
|
| 49 |
+
+ centers[25, 1] += np.random.uniform(-perturbation_scale_26th, perturbation_scale_26th)
|
| 50 |
+
+
|
| 51 |
+
+ # Ensure centers remain within the unit square after perturbation.
|
| 52 |
+
+ # Clipping to 0.01 and 0.99 prevents centers from being exactly on the boundary,
|
| 53 |
+
+ # which can sometimes lead to issues with radius calculation near walls.
|
| 54 |
+
+ centers = np.clip(centers, 0.01, 0.99)
|
| 55 |
+
+
|
| 56 |
+
# 2. Iterative Center Refinement using a Hybrid SA-Coordinate Ascent.
|
| 57 |
+
- refinement_steps = 30
|
| 58 |
+
+ # Increased refinement steps for a more thorough annealing process.
|
| 59 |
+
+ refinement_steps = 100 # Increased from 30
|
| 60 |
+
step_schedule = np.logspace(np.log10(0.05), np.log10(0.00001), refinement_steps)
|
| 61 |
+
|
| 62 |
+
# A richer 5x5 neighborhood for moves allows for finer adjustments. `(0,0)` is included.
|
| 63 |
+
move_multipliers = [-1.0, -0.5, 0.0, 0.5, 1.0]
|
| 64 |
+
moves = [(dx, dy) for dx in move_multipliers for dy in move_multipliers]
|
| 65 |
+
|
| 66 |
+
# Temperature schedule for the SA-like acceptance criterion.
|
| 67 |
+
T_initial = 0.0005 # Tuned to be sensitive to small changes in sum_radii
|
| 68 |
+
T_final = 1e-9
|
| 69 |
+
# Exponential cooling schedule based on the number of refinement steps.
|
| 70 |
+
+ # Recalculated for increased refinement_steps.
|
| 71 |
+
cooling_rate = (T_final / T_initial)**(1.0 / refinement_steps)
|
| 72 |
+
T = T_initial
|
| 73 |
+
|
| 74 |
+
for step_idx in range(refinement_steps):
|
| 75 |
+
step_size = step_schedule[step_idx]
|
| 76 |
+
|
| 77 |
+
# Dynamically adjust quick solve parameters for an efficient search.
|
| 78 |
+
if step_idx < refinement_steps // 3:
|
| 79 |
+
quick_solve_iterations = 80
|
| 80 |
+
- quick_solve_update_factor = 0.7
|
| 81 |
+
+ quick_solve_adaptive_schedule = (0.7, 0.4, 0.5) # Initial (high), Final (low), Transition Ratio
|
| 82 |
+
elif step_idx < 2 * refinement_steps // 3:
|
| 83 |
+
quick_solve_iterations = 130
|
| 84 |
+
- quick_solve_update_factor = 0.65
|
| 85 |
+
+ quick_solve_adaptive_schedule = (0.65, 0.4, 0.5) # Slightly lower initial, same final
|
| 86 |
+
else:
|
| 87 |
+
quick_solve_iterations = 200
|
| 88 |
+
- quick_solve_update_factor = 0.6
|
| 89 |
+
+ quick_solve_adaptive_schedule = (0.6, 0.4, 0.5) # Even lower initial, same final
|
| 90 |
+
|
| 91 |
+
# Iterate through circles, applying a probabilistically chosen move.
|
| 92 |
+
for i in np.random.permutation(n):
|
| 93 |
+
original_center = centers[i]
|
| 94 |
+
|
| 95 |
+
# Generate all 25 potential positions from the move set and evaluate their scores.
|
| 96 |
+
potential_positions = [np.clip(original_center + step_size * np.array(m), 0.0, 1.0) for m in moves]
|
| 97 |
+
scores = np.zeros(len(moves))
|
| 98 |
+
|
| 99 |
+
for idx, pos in enumerate(potential_positions):
|
| 100 |
+
test_centers = np.copy(centers)
|
| 101 |
+
test_centers[i] = pos
|
| 102 |
+
- test_radii = compute_max_radii(test_centers, iterations=quick_solve_iterations, update_factor=quick_solve_update_factor)
|
| 103 |
+
+ # Pass the adaptive damping schedule to the quick solver
|
| 104 |
+
+ test_radii = compute_max_radii(test_centers, iterations=quick_solve_iterations, adaptive_damping_schedule=quick_solve_adaptive_schedule)
|
| 105 |
+
scores[idx] = np.sum(test_radii)
|
| 106 |
+
|
| 107 |
+
# Select a move using a Boltzmann distribution (numerically stable softmax).
|
| 108 |
+
scores_shifted = scores - np.max(scores)
|
| 109 |
+
if T > 1e-12:
|
| 110 |
+
probs = np.exp(scores_shifted / T)
|
| 111 |
+
else: # At negligible temperature, behave greedily.
|
| 112 |
+
probs = np.zeros_like(scores)
|
| 113 |
+
probs[np.argmax(scores)] = 1.0
|
| 114 |
+
|
| 115 |
+
probs_sum = np.sum(probs)
|
| 116 |
+
if probs_sum > 0:
|
| 117 |
+
probs /= probs_sum
|
| 118 |
+
else: # Failsafe for underflow: behave greedily.
|
| 119 |
+
probs = np.zeros_like(scores)
|
| 120 |
+
probs[np.argmax(scores)] = 1.0
|
| 121 |
+
|
| 122 |
+
# Choose and apply one move based on the calculated probabilities.
|
| 123 |
+
chosen_idx = np.random.choice(len(potential_positions), p=probs)
|
| 124 |
+
centers[i] = potential_positions[chosen_idx]
|
| 125 |
+
|
| 126 |
+
# Cool the temperature for the next refinement step.
|
| 127 |
+
T *= cooling_rate
|
| 128 |
+
|
| 129 |
+
# 3. Final high-precision radius calculation on the optimized centers.
|
| 130 |
+
- # Use a proven stable update_factor and many iterations for the final solve.
|
| 131 |
+
- final_radii = compute_max_radii(centers, iterations=10000, update_factor=0.55)
|
| 132 |
+
+ # Use an adaptive damping schedule and many iterations for the final solve for maximum accuracy.
|
| 133 |
+
+ # The schedule (0.65, 0.5, 0.2) is based on successful prior implementations.
|
| 134 |
+
+ final_radii = compute_max_radii(centers, iterations=10000, adaptive_damping_schedule=(0.65, 0.5, 0.2))
|
| 135 |
+
|
| 136 |
+
return centers, final_radii
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
-def compute_max_radii(centers, iterations=5000, update_factor=0.6):
|
| 140 |
+
+def compute_max_radii(centers, iterations=5000, update_factor=0.6, adaptive_damping_schedule=None):
|
| 141 |
+
"""
|
| 142 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 143 |
+
+ This version supports an optional adaptive damping schedule.
|
| 144 |
+
|
| 145 |
+
Args:
|
| 146 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates.
|
| 147 |
+
iterations: The number of relaxation iterations to perform.
|
| 148 |
+
- update_factor: Damping factor for radius updates.
|
| 149 |
+
+ update_factor: Base damping factor for radius updates (used if adaptive_damping_schedule is None).
|
| 150 |
+
+ adaptive_damping_schedule: A tuple (initial_factor, final_factor, transition_ratio)
|
| 151 |
+
+ to linearly decay the damping factor over iterations.
|
| 152 |
+
+ If None, a fixed `update_factor` is used.
|
| 153 |
+
|
| 154 |
+
Returns:
|
| 155 |
+
np.array of shape (n) with the radius of each circle.
|
| 156 |
+
"""
|
| 157 |
+
n = centers.shape[0]
|
| 158 |
+
# Small non-zero start to prevent issues on the first iteration.
|
| 159 |
+
radii = np.full(n, 1e-6)
|
| 160 |
+
|
| 161 |
+
# Pre-calculate distances between centers.
|
| 162 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 163 |
+
# Set diagonal to infinity so a circle isn't constrained by itself.
|
| 164 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 165 |
+
|
| 166 |
+
# Pre-calculate wall distances for all circles.
|
| 167 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 168 |
+
|
| 169 |
+
- for _ in range(iterations):
|
| 170 |
+
+ for iter_idx in range(iterations): # Changed loop variable to iter_idx
|
| 171 |
+
radii_old = np.copy(radii)
|
| 172 |
+
+
|
| 173 |
+
+ # Determine current damping factor based on adaptive schedule or fixed value
|
| 174 |
+
+ if adaptive_damping_schedule is not None:
|
| 175 |
+
+ initial_uf, final_uf, transition_ratio = adaptive_damping_schedule
|
| 176 |
+
+ if iterations == 0: # Avoid division by zero
|
| 177 |
+
+ current_update_factor = final_uf
|
| 178 |
+
+ elif iter_idx < iterations * transition_ratio:
|
| 179 |
+
+ # Linearly decay from initial_uf to final_uf over the transition_ratio of iterations
|
| 180 |
+
+ current_update_factor = initial_uf - (initial_uf - final_uf) * (iter_idx / (iterations * transition_ratio))
|
| 181 |
+
+ else:
|
| 182 |
+
+ # Maintain final_uf for the remaining iterations
|
| 183 |
+
+ current_update_factor = final_uf
|
| 184 |
+
+ else:
|
| 185 |
+
+ current_update_factor = update_factor
|
| 186 |
+
|
| 187 |
+
# Calculate all potential radii based on other circles in a vectorized way.
|
| 188 |
+
# `dist_matrix - radii_old` gives a matrix of potential radii from each other circle.
|
| 189 |
+
# Taking `min(axis=1)` finds the tightest constraint for each circle.
|
| 190 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 191 |
+
|
| 192 |
+
# Combine with wall constraints.
|
| 193 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 194 |
+
|
| 195 |
+
# New radii for this iteration (must be non-negative).
|
| 196 |
+
new_r = np.maximum(0.0, potential_r)
|
| 197 |
+
|
| 198 |
+
# Dampen the update to prevent oscillations.
|
| 199 |
+
- radii = radii_old + update_factor * (new_r - radii_old)
|
| 200 |
+
+ radii = radii_old + current_update_factor * (new_r - radii_old)
|
| 201 |
+
|
| 202 |
+
# Early exit condition if radii have converged.
|
| 203 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 204 |
+
break
|
| 205 |
+
|
| 206 |
+
return radii
|
| 207 |
+
# EVOLVE-BLOCK-END
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
# This part remains fixed (not evolved)
|
| 211 |
+
def run_packing():
|
| 212 |
+
"""Run the circle packing constructor for n=26"""
|
| 213 |
+
centers, radii = construct_packing()
|
| 214 |
+
# Calculate the sum of radii
|
| 215 |
+
sum_radii = np.sum(radii)
|
| 216 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/main.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
"""Constructor-based circle packing for n=26 circles combining 5x5 grid layout
|
| 3 |
+
with iterative radius relaxation solver."""
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def construct_packing():
|
| 9 |
+
"""
|
| 10 |
+
Constructs an optimized arrangement of 26 circles by starting with a strong
|
| 11 |
+
grid-based pattern and refining center positions using a coordinate ascent
|
| 12 |
+
method with a rich neighborhood and dynamic solver parameters.
|
| 13 |
+
|
| 14 |
+
Returns:
|
| 15 |
+
A tuple containing:
|
| 16 |
+
centers: np.array of shape (26, 2) with the final optimized (x, y) coordinates
|
| 17 |
+
radii: np.array of shape (26) with the final radius of each circle
|
| 18 |
+
"""
|
| 19 |
+
n = 26
|
| 20 |
+
centers = np.zeros((n, 2))
|
| 21 |
+
|
| 22 |
+
# 1. Initial Placement: Start with a 5x5 grid + central circle pattern.
|
| 23 |
+
# Adopt the optimized margin 'd' from a successful prior for a potentially better
|
| 24 |
+
# initial grid configuration. This value is known to be effective for dense packings.
|
| 25 |
+
d = 0.09525
|
| 26 |
+
grid_coords = np.linspace(d, 1 - d, 5)
|
| 27 |
+
idx = 0
|
| 28 |
+
for x in grid_coords:
|
| 29 |
+
for y in grid_coords:
|
| 30 |
+
centers[idx] = [x, y]
|
| 31 |
+
idx += 1
|
| 32 |
+
|
| 33 |
+
# Perturb the 26th circle to break initial symmetry. This is critical for escaping local optima.
|
| 34 |
+
# Revert to the fixed, proven value instead of a random one for a stable, high-quality start.
|
| 35 |
+
centers[25] = [0.39, 0.41]
|
| 36 |
+
|
| 37 |
+
# Apply small random perturbations to all centers to help escape local optima
|
| 38 |
+
# by introducing slight initial asymmetry and exploring varied starting points.
|
| 39 |
+
# These scales are based on successful prior implementations.
|
| 40 |
+
perturbation_scale_grid = 0.005 # For the 5x5 grid circles
|
| 41 |
+
perturbation_scale_26th = 0.01 # For the 26th central circle
|
| 42 |
+
|
| 43 |
+
centers[:25, 0] += np.random.uniform(-perturbation_scale_grid, perturbation_scale_grid, 25)
|
| 44 |
+
centers[:25, 1] += np.random.uniform(-perturbation_scale_grid, perturbation_scale_grid, 25)
|
| 45 |
+
centers[25, 0] += np.random.uniform(-perturbation_scale_26th, perturbation_scale_26th)
|
| 46 |
+
centers[25, 1] += np.random.uniform(-perturbation_scale_26th, perturbation_scale_26th)
|
| 47 |
+
|
| 48 |
+
# Ensure centers remain within the unit square after perturbation.
|
| 49 |
+
# Clipping to 0.01 and 0.99 prevents centers from being exactly on the boundary,
|
| 50 |
+
# which can sometimes lead to issues with radius calculation near walls.
|
| 51 |
+
centers = np.clip(centers, 0.01, 0.99)
|
| 52 |
+
|
| 53 |
+
# 2. Iterative Center Refinement using a Hybrid SA-Coordinate Ascent.
|
| 54 |
+
# Increased refinement steps for a more thorough annealing process.
|
| 55 |
+
refinement_steps = 100 # Increased from 30
|
| 56 |
+
step_schedule = np.logspace(np.log10(0.05), np.log10(0.00001), refinement_steps)
|
| 57 |
+
|
| 58 |
+
# A richer 5x5 neighborhood for moves allows for finer adjustments. `(0,0)` is included.
|
| 59 |
+
move_multipliers = [-1.0, -0.5, 0.0, 0.5, 1.0]
|
| 60 |
+
moves = [(dx, dy) for dx in move_multipliers for dy in move_multipliers]
|
| 61 |
+
|
| 62 |
+
# Temperature schedule for the SA-like acceptance criterion.
|
| 63 |
+
T_initial = 0.0005 # Tuned to be sensitive to small changes in sum_radii
|
| 64 |
+
T_final = 1e-9
|
| 65 |
+
# Exponential cooling schedule based on the number of refinement steps.
|
| 66 |
+
# Recalculated for increased refinement_steps.
|
| 67 |
+
cooling_rate = (T_final / T_initial)**(1.0 / refinement_steps)
|
| 68 |
+
T = T_initial
|
| 69 |
+
|
| 70 |
+
for step_idx in range(refinement_steps):
|
| 71 |
+
step_size = step_schedule[step_idx]
|
| 72 |
+
|
| 73 |
+
# Dynamically adjust quick solve parameters for an efficient search.
|
| 74 |
+
if step_idx < refinement_steps // 3:
|
| 75 |
+
quick_solve_iterations = 80
|
| 76 |
+
quick_solve_adaptive_schedule = (0.7, 0.4, 0.5) # Initial (high), Final (low), Transition Ratio
|
| 77 |
+
elif step_idx < 2 * refinement_steps // 3:
|
| 78 |
+
quick_solve_iterations = 130
|
| 79 |
+
quick_solve_adaptive_schedule = (0.65, 0.4, 0.5) # Slightly lower initial, same final
|
| 80 |
+
else:
|
| 81 |
+
quick_solve_iterations = 200
|
| 82 |
+
quick_solve_adaptive_schedule = (0.6, 0.4, 0.5) # Even lower initial, same final
|
| 83 |
+
|
| 84 |
+
# Iterate through circles, applying a probabilistically chosen move.
|
| 85 |
+
for i in np.random.permutation(n):
|
| 86 |
+
original_center = centers[i]
|
| 87 |
+
|
| 88 |
+
# Generate all 25 potential positions from the move set and evaluate their scores.
|
| 89 |
+
potential_positions = [np.clip(original_center + step_size * np.array(m), 0.0, 1.0) for m in moves]
|
| 90 |
+
scores = np.zeros(len(moves))
|
| 91 |
+
|
| 92 |
+
for idx, pos in enumerate(potential_positions):
|
| 93 |
+
test_centers = np.copy(centers)
|
| 94 |
+
test_centers[i] = pos
|
| 95 |
+
# Pass the adaptive damping schedule to the quick solver
|
| 96 |
+
test_radii = compute_max_radii(test_centers, iterations=quick_solve_iterations, adaptive_damping_schedule=quick_solve_adaptive_schedule)
|
| 97 |
+
scores[idx] = np.sum(test_radii)
|
| 98 |
+
|
| 99 |
+
# Select a move using a Boltzmann distribution (numerically stable softmax).
|
| 100 |
+
scores_shifted = scores - np.max(scores)
|
| 101 |
+
if T > 1e-12:
|
| 102 |
+
probs = np.exp(scores_shifted / T)
|
| 103 |
+
else: # At negligible temperature, behave greedily.
|
| 104 |
+
probs = np.zeros_like(scores)
|
| 105 |
+
probs[np.argmax(scores)] = 1.0
|
| 106 |
+
|
| 107 |
+
probs_sum = np.sum(probs)
|
| 108 |
+
if probs_sum > 0:
|
| 109 |
+
probs /= probs_sum
|
| 110 |
+
else: # Failsafe for underflow: behave greedily.
|
| 111 |
+
probs = np.zeros_like(scores)
|
| 112 |
+
probs[np.argmax(scores)] = 1.0
|
| 113 |
+
|
| 114 |
+
# Choose and apply one move based on the calculated probabilities.
|
| 115 |
+
chosen_idx = np.random.choice(len(potential_positions), p=probs)
|
| 116 |
+
centers[i] = potential_positions[chosen_idx]
|
| 117 |
+
|
| 118 |
+
# Cool the temperature for the next refinement step.
|
| 119 |
+
T *= cooling_rate
|
| 120 |
+
|
| 121 |
+
# 3. Final high-precision radius calculation on the optimized centers.
|
| 122 |
+
# Use an adaptive damping schedule and many iterations for the final solve for maximum accuracy.
|
| 123 |
+
# The schedule (0.65, 0.5, 0.2) is based on successful prior implementations.
|
| 124 |
+
final_radii = compute_max_radii(centers, iterations=10000, adaptive_damping_schedule=(0.65, 0.5, 0.2))
|
| 125 |
+
|
| 126 |
+
return centers, final_radii
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def compute_max_radii(centers, iterations=5000, update_factor=0.6, adaptive_damping_schedule=None):
|
| 130 |
+
"""
|
| 131 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 132 |
+
This version supports an optional adaptive damping schedule.
|
| 133 |
+
|
| 134 |
+
Args:
|
| 135 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates.
|
| 136 |
+
iterations: The number of relaxation iterations to perform.
|
| 137 |
+
update_factor: Base damping factor for radius updates (used if adaptive_damping_schedule is None).
|
| 138 |
+
adaptive_damping_schedule: A tuple (initial_factor, final_factor, transition_ratio)
|
| 139 |
+
to linearly decay the damping factor over iterations.
|
| 140 |
+
If None, a fixed `update_factor` is used.
|
| 141 |
+
|
| 142 |
+
Returns:
|
| 143 |
+
np.array of shape (n) with the radius of each circle.
|
| 144 |
+
"""
|
| 145 |
+
n = centers.shape[0]
|
| 146 |
+
# Small non-zero start to prevent issues on the first iteration.
|
| 147 |
+
radii = np.full(n, 1e-6)
|
| 148 |
+
|
| 149 |
+
# Pre-calculate distances between centers.
|
| 150 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 151 |
+
# Set diagonal to infinity so a circle isn't constrained by itself.
|
| 152 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 153 |
+
|
| 154 |
+
# Pre-calculate wall distances for all circles.
|
| 155 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 156 |
+
|
| 157 |
+
for iter_idx in range(iterations): # Changed loop variable to iter_idx
|
| 158 |
+
radii_old = np.copy(radii)
|
| 159 |
+
|
| 160 |
+
# Determine current damping factor based on adaptive schedule or fixed value
|
| 161 |
+
if adaptive_damping_schedule is not None:
|
| 162 |
+
initial_uf, final_uf, transition_ratio = adaptive_damping_schedule
|
| 163 |
+
if iterations == 0: # Avoid division by zero
|
| 164 |
+
current_update_factor = final_uf
|
| 165 |
+
elif iter_idx < iterations * transition_ratio:
|
| 166 |
+
# Linearly decay from initial_uf to final_uf over the transition_ratio of iterations
|
| 167 |
+
current_update_factor = initial_uf - (initial_uf - final_uf) * (iter_idx / (iterations * transition_ratio))
|
| 168 |
+
else:
|
| 169 |
+
# Maintain final_uf for the remaining iterations
|
| 170 |
+
current_update_factor = final_uf
|
| 171 |
+
else:
|
| 172 |
+
current_update_factor = update_factor
|
| 173 |
+
|
| 174 |
+
# Calculate all potential radii based on other circles in a vectorized way.
|
| 175 |
+
# `dist_matrix - radii_old` gives a matrix of potential radii from each other circle.
|
| 176 |
+
# Taking `min(axis=1)` finds the tightest constraint for each circle.
|
| 177 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 178 |
+
|
| 179 |
+
# Combine with wall constraints.
|
| 180 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 181 |
+
|
| 182 |
+
# New radii for this iteration (must be non-negative).
|
| 183 |
+
new_r = np.maximum(0.0, potential_r)
|
| 184 |
+
|
| 185 |
+
# Dampen the update to prevent oscillations.
|
| 186 |
+
radii = radii_old + current_update_factor * (new_r - radii_old)
|
| 187 |
+
|
| 188 |
+
# Early exit condition if radii have converged.
|
| 189 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 190 |
+
break
|
| 191 |
+
|
| 192 |
+
return radii
|
| 193 |
+
# EVOLVE-BLOCK-END
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
# This part remains fixed (not evolved)
|
| 197 |
+
def run_packing():
|
| 198 |
+
"""Run the circle packing constructor for n=26"""
|
| 199 |
+
centers, radii = construct_packing()
|
| 200 |
+
# Calculate the sum of radii
|
| 201 |
+
sum_radii = np.sum(radii)
|
| 202 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/original.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
"""Constructor-based circle packing for n=26 circles combining 5x5 grid layout
|
| 3 |
+
with iterative radius relaxation solver."""
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def construct_packing():
|
| 9 |
+
"""
|
| 10 |
+
Constructs an optimized arrangement of 26 circles by starting with a strong
|
| 11 |
+
grid-based pattern and refining center positions using a coordinate ascent
|
| 12 |
+
method with a rich neighborhood and dynamic solver parameters.
|
| 13 |
+
|
| 14 |
+
Returns:
|
| 15 |
+
A tuple containing:
|
| 16 |
+
centers: np.array of shape (26, 2) with the final optimized (x, y) coordinates
|
| 17 |
+
radii: np.array of shape (26) with the final radius of each circle
|
| 18 |
+
"""
|
| 19 |
+
n = 26
|
| 20 |
+
centers = np.zeros((n, 2))
|
| 21 |
+
|
| 22 |
+
# 1. Initial Placement: Start with a 5x5 grid + central circle pattern.
|
| 23 |
+
# Adopt the optimized margin 'd' from a successful prior for a potentially better
|
| 24 |
+
# initial grid configuration. This value is known to be effective for dense packings.
|
| 25 |
+
d = 0.09525
|
| 26 |
+
grid_coords = np.linspace(d, 1 - d, 5)
|
| 27 |
+
idx = 0
|
| 28 |
+
for x in grid_coords:
|
| 29 |
+
for y in grid_coords:
|
| 30 |
+
centers[idx] = [x, y]
|
| 31 |
+
idx += 1
|
| 32 |
+
|
| 33 |
+
# Perturb the 26th circle to break initial symmetry. This is critical for escaping local optima.
|
| 34 |
+
# Revert to the fixed, proven value instead of a random one for a stable, high-quality start.
|
| 35 |
+
centers[25] = [0.39, 0.41]
|
| 36 |
+
|
| 37 |
+
# 2. Iterative Center Refinement using a Hybrid SA-Coordinate Ascent.
|
| 38 |
+
refinement_steps = 30
|
| 39 |
+
step_schedule = np.logspace(np.log10(0.05), np.log10(0.00001), refinement_steps)
|
| 40 |
+
|
| 41 |
+
# A richer 5x5 neighborhood for moves allows for finer adjustments. `(0,0)` is included.
|
| 42 |
+
move_multipliers = [-1.0, -0.5, 0.0, 0.5, 1.0]
|
| 43 |
+
moves = [(dx, dy) for dx in move_multipliers for dy in move_multipliers]
|
| 44 |
+
|
| 45 |
+
# Temperature schedule for the SA-like acceptance criterion.
|
| 46 |
+
T_initial = 0.0005 # Tuned to be sensitive to small changes in sum_radii
|
| 47 |
+
T_final = 1e-9
|
| 48 |
+
# Exponential cooling schedule based on the number of refinement steps.
|
| 49 |
+
cooling_rate = (T_final / T_initial)**(1.0 / refinement_steps)
|
| 50 |
+
T = T_initial
|
| 51 |
+
|
| 52 |
+
for step_idx in range(refinement_steps):
|
| 53 |
+
step_size = step_schedule[step_idx]
|
| 54 |
+
|
| 55 |
+
# Dynamically adjust quick solve parameters for an efficient search.
|
| 56 |
+
if step_idx < refinement_steps // 3:
|
| 57 |
+
quick_solve_iterations = 80
|
| 58 |
+
quick_solve_update_factor = 0.7
|
| 59 |
+
elif step_idx < 2 * refinement_steps // 3:
|
| 60 |
+
quick_solve_iterations = 130
|
| 61 |
+
quick_solve_update_factor = 0.65
|
| 62 |
+
else:
|
| 63 |
+
quick_solve_iterations = 200
|
| 64 |
+
quick_solve_update_factor = 0.6
|
| 65 |
+
|
| 66 |
+
# Iterate through circles, applying a probabilistically chosen move.
|
| 67 |
+
for i in np.random.permutation(n):
|
| 68 |
+
original_center = centers[i]
|
| 69 |
+
|
| 70 |
+
# Generate all 25 potential positions from the move set and evaluate their scores.
|
| 71 |
+
potential_positions = [np.clip(original_center + step_size * np.array(m), 0.0, 1.0) for m in moves]
|
| 72 |
+
scores = np.zeros(len(moves))
|
| 73 |
+
|
| 74 |
+
for idx, pos in enumerate(potential_positions):
|
| 75 |
+
test_centers = np.copy(centers)
|
| 76 |
+
test_centers[i] = pos
|
| 77 |
+
test_radii = compute_max_radii(test_centers, iterations=quick_solve_iterations, update_factor=quick_solve_update_factor)
|
| 78 |
+
scores[idx] = np.sum(test_radii)
|
| 79 |
+
|
| 80 |
+
# Select a move using a Boltzmann distribution (numerically stable softmax).
|
| 81 |
+
scores_shifted = scores - np.max(scores)
|
| 82 |
+
if T > 1e-12:
|
| 83 |
+
probs = np.exp(scores_shifted / T)
|
| 84 |
+
else: # At negligible temperature, behave greedily.
|
| 85 |
+
probs = np.zeros_like(scores)
|
| 86 |
+
probs[np.argmax(scores)] = 1.0
|
| 87 |
+
|
| 88 |
+
probs_sum = np.sum(probs)
|
| 89 |
+
if probs_sum > 0:
|
| 90 |
+
probs /= probs_sum
|
| 91 |
+
else: # Failsafe for underflow: behave greedily.
|
| 92 |
+
probs = np.zeros_like(scores)
|
| 93 |
+
probs[np.argmax(scores)] = 1.0
|
| 94 |
+
|
| 95 |
+
# Choose and apply one move based on the calculated probabilities.
|
| 96 |
+
chosen_idx = np.random.choice(len(potential_positions), p=probs)
|
| 97 |
+
centers[i] = potential_positions[chosen_idx]
|
| 98 |
+
|
| 99 |
+
# Cool the temperature for the next refinement step.
|
| 100 |
+
T *= cooling_rate
|
| 101 |
+
|
| 102 |
+
# 3. Final high-precision radius calculation on the optimized centers.
|
| 103 |
+
# Use a proven stable update_factor and many iterations for the final solve.
|
| 104 |
+
final_radii = compute_max_radii(centers, iterations=10000, update_factor=0.55)
|
| 105 |
+
|
| 106 |
+
return centers, final_radii
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def compute_max_radii(centers, iterations=5000, update_factor=0.6):
|
| 110 |
+
"""
|
| 111 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 112 |
+
|
| 113 |
+
Args:
|
| 114 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates.
|
| 115 |
+
iterations: The number of relaxation iterations to perform.
|
| 116 |
+
update_factor: Damping factor for radius updates.
|
| 117 |
+
|
| 118 |
+
Returns:
|
| 119 |
+
np.array of shape (n) with the radius of each circle.
|
| 120 |
+
"""
|
| 121 |
+
n = centers.shape[0]
|
| 122 |
+
# Small non-zero start to prevent issues on the first iteration.
|
| 123 |
+
radii = np.full(n, 1e-6)
|
| 124 |
+
|
| 125 |
+
# Pre-calculate distances between centers.
|
| 126 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 127 |
+
# Set diagonal to infinity so a circle isn't constrained by itself.
|
| 128 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 129 |
+
|
| 130 |
+
# Pre-calculate wall distances for all circles.
|
| 131 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 132 |
+
|
| 133 |
+
for _ in range(iterations):
|
| 134 |
+
radii_old = np.copy(radii)
|
| 135 |
+
|
| 136 |
+
# Calculate all potential radii based on other circles in a vectorized way.
|
| 137 |
+
# `dist_matrix - radii_old` gives a matrix of potential radii from each other circle.
|
| 138 |
+
# Taking `min(axis=1)` finds the tightest constraint for each circle.
|
| 139 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 140 |
+
|
| 141 |
+
# Combine with wall constraints.
|
| 142 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 143 |
+
|
| 144 |
+
# New radii for this iteration (must be non-negative).
|
| 145 |
+
new_r = np.maximum(0.0, potential_r)
|
| 146 |
+
|
| 147 |
+
# Dampen the update to prevent oscillations.
|
| 148 |
+
radii = radii_old + update_factor * (new_r - radii_old)
|
| 149 |
+
|
| 150 |
+
# Early exit condition if radii have converged.
|
| 151 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 152 |
+
break
|
| 153 |
+
|
| 154 |
+
return radii
|
| 155 |
+
# EVOLVE-BLOCK-END
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
# This part remains fixed (not evolved)
|
| 159 |
+
def run_packing():
|
| 160 |
+
"""Run the circle packing constructor for n=26"""
|
| 161 |
+
centers, radii = construct_packing()
|
| 162 |
+
# Calculate the sum of radii
|
| 163 |
+
sum_radii = np.sum(radii)
|
| 164 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/results/metrics.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 2.5235511803567197,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 2.5235511803567197,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.0883, 0.0883)\n centers[1] = (0.1180, 0.2924)\n centers[2] = (0.0934, 0.5023)\n centers[3] = (0.0973, 0.6929)\n centers[4] = (0.1242, 0.8942)\n centers[5] = (0.3013, 0.1279)\n centers[6] = (0.2995, 0.3217)\n centers[7] = (0.2691, 0.5199)\n centers[8] = (0.2959, 0.7028)\n centers[9] = (0.3291, 0.9008)\n centers[10] = (0.5016, 0.0784)\n centers[11] = (0.5025, 0.2788)\n centers[12] = (0.5164, 0.5183)\n centers[13] = (0.4966, 0.7146)\n centers[14] = (0.5221, 0.9062)\n centers[15] = (0.6876, 0.1103)\n centers[16] = (0.7166, 0.3134)\n centers[17] = (0.7085, 0.5030)\n centers[18] = (0.6989, 0.7029)\n centers[19] = (0.7130, 0.9029)\n centers[20] = (0.8969, 0.0962)\n centers[21] = (0.9048, 0.2874)\n centers[22] = (0.9011, 0.4814)\n centers[23] = (0.9005, 0.6799)\n centers[24] = (0.9041, 0.8753)\n centers[25] = (0.3829, 0.4236)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 2.5235511803567197
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 32.92109573353082,
|
| 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": 1770405591.7633276,
|
| 24 |
+
"generation": 102
|
| 25 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_102/search_replace.txt
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<NAME>
|
| 2 |
+
adaptive_damping_in_solver
|
| 3 |
+
</NAME>
|
| 4 |
+
|
| 5 |
+
<DESCRIPTION>
|
| 6 |
+
The `compute_max_radii` function is the core solver for determining circle radii.
|
| 7 |
+
Currently, it uses a single, fixed `update_factor` throughout its iterations.
|
| 8 |
+
This edit enhances `compute_max_radii` by allowing an optional `adaptive_damping_schedule`
|
| 9 |
+
tuple `(initial_factor, final_factor, transition_ratio)`. If provided, the damping
|
| 10 |
+
factor will linearly decay from `initial_factor` to `final_factor` over a
|
| 11 |
+
fraction `transition_ratio` of the total iterations, and then remain at `final_factor`.
|
| 12 |
+
This provides more nuanced control over convergence, preventing oscillations
|
| 13 |
+
early on and promoting stability in later stages, which can lead to higher precision
|
| 14 |
+
and better overall radii sums.
|
| 15 |
+
</DESCRIPTION>
|
| 16 |
+
|
| 17 |
+
<DIFF>
|
| 18 |
+
<<<<<<< SEARCH
|
| 19 |
+
def compute_max_radii(centers, iterations=5000, update_factor=0.6):
|
| 20 |
+
"""
|
| 21 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 22 |
+
|
| 23 |
+
Args:
|
| 24 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates.
|
| 25 |
+
iterations: The number of relaxation iterations to perform.
|
| 26 |
+
update_factor: Damping factor for radius updates.
|
| 27 |
+
|
| 28 |
+
Returns:
|
| 29 |
+
np.array of shape (n) with the radius of each circle.
|
| 30 |
+
"""
|
| 31 |
+
n = centers.shape[0]
|
| 32 |
+
# Small non-zero start to prevent issues on the first iteration.
|
| 33 |
+
radii = np.full(n, 1e-6)
|
| 34 |
+
|
| 35 |
+
# Pre-calculate distances between centers.
|
| 36 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 37 |
+
# Set diagonal to infinity so a circle isn't constrained by itself.
|
| 38 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 39 |
+
|
| 40 |
+
# Pre-calculate wall distances for all circles.
|
| 41 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 42 |
+
|
| 43 |
+
for _ in range(iterations):
|
| 44 |
+
radii_old = np.copy(radii)
|
| 45 |
+
|
| 46 |
+
# Calculate all potential radii based on other circles in a vectorized way.
|
| 47 |
+
# `dist_matrix - radii_old` gives a matrix of potential radii from each other circle.
|
| 48 |
+
# Taking `min(axis=1)` finds the tightest constraint for each circle.
|
| 49 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 50 |
+
|
| 51 |
+
# Combine with wall constraints.
|
| 52 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 53 |
+
|
| 54 |
+
# New radii for this iteration (must be non-negative).
|
| 55 |
+
new_r = np.maximum(0.0, potential_r)
|
| 56 |
+
|
| 57 |
+
# Dampen the update to prevent oscillations.
|
| 58 |
+
radii = radii_old + update_factor * (new_r - radii_old)
|
| 59 |
+
|
| 60 |
+
# Early exit condition if radii have converged.
|
| 61 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 62 |
+
break
|
| 63 |
+
|
| 64 |
+
return radii
|
| 65 |
+
=======
|
| 66 |
+
def compute_max_radii(centers, iterations=5000, update_factor=0.6, adaptive_damping_schedule=None):
|
| 67 |
+
"""
|
| 68 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 69 |
+
This version supports an optional adaptive damping schedule.
|
| 70 |
+
|
| 71 |
+
Args:
|
| 72 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates.
|
| 73 |
+
iterations: The number of relaxation iterations to perform.
|
| 74 |
+
update_factor: Base damping factor for radius updates (used if adaptive_damping_schedule is None).
|
| 75 |
+
adaptive_damping_schedule: A tuple (initial_factor, final_factor, transition_ratio)
|
| 76 |
+
to linearly decay the damping factor over iterations.
|
| 77 |
+
If None, a fixed `update_factor` is used.
|
| 78 |
+
|
| 79 |
+
Returns:
|
| 80 |
+
np.array of shape (n) with the radius of each circle.
|
| 81 |
+
"""
|
| 82 |
+
n = centers.shape[0]
|
| 83 |
+
# Small non-zero start to prevent issues on the first iteration.
|
| 84 |
+
radii = np.full(n, 1e-6)
|
| 85 |
+
|
| 86 |
+
# Pre-calculate distances between centers.
|
| 87 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 88 |
+
# Set diagonal to infinity so a circle isn't constrained by itself.
|
| 89 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 90 |
+
|
| 91 |
+
# Pre-calculate wall distances for all circles.
|
| 92 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 93 |
+
|
| 94 |
+
for iter_idx in range(iterations): # Changed loop variable to iter_idx
|
| 95 |
+
radii_old = np.copy(radii)
|
| 96 |
+
|
| 97 |
+
# Determine current damping factor based on adaptive schedule or fixed value
|
| 98 |
+
if adaptive_damping_schedule is not None:
|
| 99 |
+
initial_uf, final_uf, transition_ratio = adaptive_damping_schedule
|
| 100 |
+
if iterations == 0: # Avoid division by zero
|
| 101 |
+
current_update_factor = final_uf
|
| 102 |
+
elif iter_idx < iterations * transition_ratio:
|
| 103 |
+
# Linearly decay from initial_uf to final_uf over the transition_ratio of iterations
|
| 104 |
+
current_update_factor = initial_uf - (initial_uf - final_uf) * (iter_idx / (iterations * transition_ratio))
|
| 105 |
+
else:
|
| 106 |
+
# Maintain final_uf for the remaining iterations
|
| 107 |
+
current_update_factor = final_uf
|
| 108 |
+
else:
|
| 109 |
+
current_update_factor = update_factor
|
| 110 |
+
|
| 111 |
+
# Calculate all potential radii based on other circles in a vectorized way.
|
| 112 |
+
# `dist_matrix - radii_old` gives a matrix of potential radii from each other circle.
|
| 113 |
+
# Taking `min(axis=1)` finds the tightest constraint for each circle.
|
| 114 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 115 |
+
|
| 116 |
+
# Combine with wall constraints.
|
| 117 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 118 |
+
|
| 119 |
+
# New radii for this iteration (must be non-negative).
|
| 120 |
+
new_r = np.maximum(0.0, potential_r)
|
| 121 |
+
|
| 122 |
+
# Dampen the update to prevent oscillations.
|
| 123 |
+
radii = radii_old + current_update_factor * (new_r - radii_old)
|
| 124 |
+
|
| 125 |
+
# Early exit condition if radii have converged.
|
| 126 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 127 |
+
break
|
| 128 |
+
|
| 129 |
+
return radii
|
| 130 |
+
>>>>>>> REPLACE
|
| 131 |
+
</DIFF>
|
| 132 |
+
|
| 133 |
+
<NAME>
|
| 134 |
+
initial_random_perturbations
|
| 135 |
+
</NAME>
|
| 136 |
+
|
| 137 |
+
<DESCRIPTION>
|
| 138 |
+
To enhance the exploration of the solution space and prevent premature convergence
|
| 139 |
+
to a local optimum, small random perturbations are reintroduced to all circle
|
| 140 |
+
centers immediately after their initial grid placement. This slightly breaks
|
| 141 |
+
the initial symmetry and provides a diverse set of starting configurations for
|
| 142 |
+
the subsequent refinement process. Different perturbation scales are applied
|
| 143 |
+
to the main grid circles and the 26th central circle to reflect their distinct roles.
|
| 144 |
+
Centers are clipped to ensure they remain within the unit square. This strategy
|
| 145 |
+
was present in prior successful programs and can improve the final sum of radii.
|
| 146 |
+
</DESCRIPTION>
|
| 147 |
+
|
| 148 |
+
<DIFF>
|
| 149 |
+
<<<<<<< SEARCH
|
| 150 |
+
# Perturb the 26th circle to break initial symmetry. This is critical for escaping local optima.
|
| 151 |
+
# Revert to the fixed, proven value instead of a random one for a stable, high-quality start.
|
| 152 |
+
centers[25] = [0.39, 0.41]
|
| 153 |
+
|
| 154 |
+
# 2. Iterative Center Refinement using a Hybrid SA-Coordinate Ascent.
|
| 155 |
+
refinement_steps = 30
|
| 156 |
+
step_schedule = np.logspace(np.log10(0.05), np.log10(0.00001), refinement_steps)
|
| 157 |
+
=======
|
| 158 |
+
# Perturb the 26th circle to break initial symmetry. This is critical for escaping local optima.
|
| 159 |
+
# Revert to the fixed, proven value instead of a random one for a stable, high-quality start.
|
| 160 |
+
centers[25] = [0.39, 0.41]
|
| 161 |
+
|
| 162 |
+
# Apply small random perturbations to all centers to help escape local optima
|
| 163 |
+
# by introducing slight initial asymmetry and exploring varied starting points.
|
| 164 |
+
# These scales are based on successful prior implementations.
|
| 165 |
+
perturbation_scale_grid = 0.005 # For the 5x5 grid circles
|
| 166 |
+
perturbation_scale_26th = 0.01 # For the 26th central circle
|
| 167 |
+
|
| 168 |
+
centers[:25, 0] += np.random.uniform(-perturbation_scale_grid, perturbation_scale_grid, 25)
|
| 169 |
+
centers[:25, 1] += np.random.uniform(-perturbation_scale_grid, perturbation_scale_grid, 25)
|
| 170 |
+
centers[25, 0] += np.random.uniform(-perturbation_scale_26th, perturbation_scale_26th)
|
| 171 |
+
centers[25, 1] += np.random.uniform(-perturbation_scale_26th, perturbation_scale_26th)
|
| 172 |
+
|
| 173 |
+
# Ensure centers remain within the unit square after perturbation.
|
| 174 |
+
# Clipping to 0.01 and 0.99 prevents centers from being exactly on the boundary,
|
| 175 |
+
# which can sometimes lead to issues with radius calculation near walls.
|
| 176 |
+
centers = np.clip(centers, 0.01, 0.99)
|
| 177 |
+
|
| 178 |
+
# 2. Iterative Center Refinement using a Hybrid SA-Coordinate Ascent.
|
| 179 |
+
refinement_steps = 30
|
| 180 |
+
step_schedule = np.logspace(np.log10(0.05), np.log10(0.00001), refinement_steps)
|
| 181 |
+
>>>>>>> REPLACE
|
| 182 |
+
</DIFF>
|
| 183 |
+
|
| 184 |
+
<NAME>
|
| 185 |
+
slower_annealing_schedule
|
| 186 |
+
</NAME>
|
| 187 |
+
|
| 188 |
+
<DESCRIPTION>
|
| 189 |
+
The current simulated annealing component cools very rapidly, potentially leading
|
| 190 |
+
to premature convergence to local optima. This edit proposes to increase the
|
| 191 |
+
number of `refinement_steps` from 30 to 100 and re-calculate the `cooling_rate`
|
| 192 |
+
accordingly. This change allows for a significantly slower cooling process and
|
| 193 |
+
more iterations at higher "temperatures," enabling broader exploration of the
|
| 194 |
+
solution space and a greater chance to escape local optima. While increasing
|
| 195 |
+
computation, the improved search quality is expected to yield a higher sum of radii.
|
| 196 |
+
</DESCRIPTION>
|
| 197 |
+
|
| 198 |
+
<DIFF>
|
| 199 |
+
<<<<<<< SEARCH
|
| 200 |
+
# 2. Iterative Center Refinement using a Hybrid SA-Coordinate Ascent.
|
| 201 |
+
refinement_steps = 30
|
| 202 |
+
step_schedule = np.logspace(np.log10(0.05), np.log10(0.00001), refinement_steps)
|
| 203 |
+
|
| 204 |
+
# A richer 5x5 neighborhood for moves allows for finer adjustments. `(0,0)` is included.
|
| 205 |
+
move_multipliers = [-1.0, -0.5, 0.0, 0.5, 1.0]
|
| 206 |
+
moves = [(dx, dy) for dx in move_multipliers for dy in move_multipliers]
|
| 207 |
+
|
| 208 |
+
# Temperature schedule for the SA-like acceptance criterion.
|
| 209 |
+
T_initial = 0.0005 # Tuned to be sensitive to small changes in sum_radii
|
| 210 |
+
T_final = 1e-9
|
| 211 |
+
# Exponential cooling schedule based on the number of refinement steps.
|
| 212 |
+
cooling_rate = (T_final / T_initial)**(1.0 / refinement_steps)
|
| 213 |
+
T = T_initial
|
| 214 |
+
=======
|
| 215 |
+
# 2. Iterative Center Refinement using a Hybrid SA-Coordinate Ascent.
|
| 216 |
+
# Increased refinement steps for a more thorough annealing process.
|
| 217 |
+
refinement_steps = 100 # Increased from 30
|
| 218 |
+
step_schedule = np.logspace(np.log10(0.05), np.log10(0.00001), refinement_steps)
|
| 219 |
+
|
| 220 |
+
# A richer 5x5 neighborhood for moves allows for finer adjustments. `(0,0)` is included.
|
| 221 |
+
move_multipliers = [-1.0, -0.5, 0.0, 0.5, 1.0]
|
| 222 |
+
moves = [(dx, dy) for dx in move_multipliers for dy in move_multipliers]
|
| 223 |
+
|
| 224 |
+
# Temperature schedule for the SA-like acceptance criterion.
|
| 225 |
+
T_initial = 0.0005 # Tuned to be sensitive to small changes in sum_radii
|
| 226 |
+
T_final = 1e-9
|
| 227 |
+
# Exponential cooling schedule based on the number of refinement steps.
|
| 228 |
+
# Recalculated for increased refinement_steps.
|
| 229 |
+
cooling_rate = (T_final / T_initial)**(1.0 / refinement_steps)
|
| 230 |
+
T = T_initial
|
| 231 |
+
>>>>>>> REPLACE
|
| 232 |
+
</DIFF>
|
| 233 |
+
|
| 234 |
+
<NAME>
|
| 235 |
+
adaptive_quick_solve_damping
|
| 236 |
+
</NAME>
|
| 237 |
+
|
| 238 |
+
<DESCRIPTION>
|
| 239 |
+
The intermediate `compute_max_radii` calls are now enhanced by using
|
| 240 |
+
an adaptive damping schedule rather than a fixed `update_factor`. This allows
|
| 241 |
+
the radius solver to be more aggressive early in its own iterations (higher
|
| 242 |
+
initial damping) and then settle into a more stable convergence (lower final damping).
|
| 243 |
+
This change makes the quick radius evaluations more accurate and robust, which
|
| 244 |
+
can better guide the center refinement process. The schedule for each stage
|
| 245 |
+
is tuned based on the fixed factors previously used, ensuring a smooth transition
|
| 246 |
+
and potentially better overall convergence for the main optimization loop.
|
| 247 |
+
</DESCRIPTION>
|
| 248 |
+
|
| 249 |
+
<DIFF>
|
| 250 |
+
<<<<<<< SEARCH
|
| 251 |
+
if step_idx < refinement_steps // 3:
|
| 252 |
+
quick_solve_iterations = 80
|
| 253 |
+
quick_solve_update_factor = 0.7
|
| 254 |
+
elif step_idx < 2 * refinement_steps // 3:
|
| 255 |
+
quick_solve_iterations = 130
|
| 256 |
+
quick_solve_update_factor = 0.65
|
| 257 |
+
else:
|
| 258 |
+
quick_solve_iterations = 200
|
| 259 |
+
quick_solve_update_factor = 0.6
|
| 260 |
+
|
| 261 |
+
# Iterate through circles, applying a probabilistically chosen move.
|
| 262 |
+
for i in np.random.permutation(n):
|
| 263 |
+
original_center = centers[i]
|
| 264 |
+
|
| 265 |
+
# Generate all 25 potential positions from the move set and evaluate their scores.
|
| 266 |
+
potential_positions = [np.clip(original_center + step_size * np.array(m), 0.0, 1.0) for m in moves]
|
| 267 |
+
scores = np.zeros(len(moves))
|
| 268 |
+
|
| 269 |
+
for idx, pos in enumerate(potential_positions):
|
| 270 |
+
test_centers = np.copy(centers)
|
| 271 |
+
test_centers[i] = pos
|
| 272 |
+
test_radii = compute_max_radii(test_centers, iterations=quick_solve_iterations, update_factor=quick_solve_update_factor)
|
| 273 |
+
scores[idx] = np.sum(test_radii)
|
| 274 |
+
=======
|
| 275 |
+
if step_idx < refinement_steps // 3:
|
| 276 |
+
quick_solve_iterations = 80
|
| 277 |
+
quick_solve_adaptive_schedule = (0.7, 0.4, 0.5) # Initial (high), Final (low), Transition Ratio
|
| 278 |
+
elif step_idx < 2 * refinement_steps // 3:
|
| 279 |
+
quick_solve_iterations = 130
|
| 280 |
+
quick_solve_adaptive_schedule = (0.65, 0.4, 0.5) # Slightly lower initial, same final
|
| 281 |
+
else:
|
| 282 |
+
quick_solve_iterations = 200
|
| 283 |
+
quick_solve_adaptive_schedule = (0.6, 0.4, 0.5) # Even lower initial, same final
|
| 284 |
+
|
| 285 |
+
# Iterate through circles, applying a probabilistically chosen move.
|
| 286 |
+
for i in np.random.permutation(n):
|
| 287 |
+
original_center = centers[i]
|
| 288 |
+
|
| 289 |
+
# Generate all 25 potential positions from the move set and evaluate their scores.
|
| 290 |
+
potential_positions = [np.clip(original_center + step_size * np.array(m), 0.0, 1.0) for m in moves]
|
| 291 |
+
scores = np.zeros(len(moves))
|
| 292 |
+
|
| 293 |
+
for idx, pos in enumerate(potential_positions):
|
| 294 |
+
test_centers = np.copy(centers)
|
| 295 |
+
test_centers[i] = pos
|
| 296 |
+
# Pass the adaptive damping schedule to the quick solver
|
| 297 |
+
test_radii = compute_max_radii(test_centers, iterations=quick_solve_iterations, adaptive_damping_schedule=quick_solve_adaptive_schedule)
|
| 298 |
+
scores[idx] = np.sum(test_radii)
|
| 299 |
+
>>>>>>> REPLACE
|
| 300 |
+
</DIFF>
|
| 301 |
+
|
| 302 |
+
<NAME>
|
| 303 |
+
adaptive_final_solve_damping
|
| 304 |
+
</NAME>
|
| 305 |
+
|
| 306 |
+
<DESCRIPTION>
|
| 307 |
+
The final high-precision radius calculation is crucial for the overall sum of radii.
|
| 308 |
+
By applying an adaptive damping schedule to this step, the solver can achieve
|
| 309 |
+
a more stable and accurate convergence. The `(0.65, 0.5, 0.2)` schedule (initial, final, transition_ratio)
|
| 310 |
+
is derived from previously successful implementations, allowing for faster updates
|
| 311 |
+
early in the final solve and very precise fine-tuning towards the end.
|
| 312 |
+
This ensures the reported sum of radii is as accurate as possible for the optimized centers.
|
| 313 |
+
</DESCRIPTION>
|
| 314 |
+
|
| 315 |
+
<DIFF>
|
| 316 |
+
<<<<<<< SEARCH
|
| 317 |
+
# 3. Final high-precision radius calculation on the optimized centers.
|
| 318 |
+
# Use a proven stable update_factor and many iterations for the final solve.
|
| 319 |
+
final_radii = compute_max_radii(centers, iterations=10000, update_factor=0.55)
|
| 320 |
+
=======
|
| 321 |
+
# 3. Final high-precision radius calculation on the optimized centers.
|
| 322 |
+
# Use an adaptive damping schedule and many iterations for the final solve for maximum accuracy.
|
| 323 |
+
# The schedule (0.65, 0.5, 0.2) is based on successful prior implementations.
|
| 324 |
+
final_radii = compute_max_radii(centers, iterations=10000, adaptive_damping_schedule=(0.65, 0.5, 0.2))
|
| 325 |
+
>>>>>>> REPLACE
|
| 326 |
+
</DIFF>
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_103/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (6.6 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_103/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": false,
|
| 3 |
+
"error": "operands could not be broadcast together with shapes (3,) (26,) "
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_103/results/metrics.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 0.0,
|
| 3 |
+
"correct": false,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 0.0,
|
| 6 |
+
"execution_time_mean": 0.0,
|
| 7 |
+
"execution_time_std": 0.0,
|
| 8 |
+
"num_successful_runs": 0,
|
| 9 |
+
"num_valid_runs": 0,
|
| 10 |
+
"num_invalid_runs": 0,
|
| 11 |
+
"all_validation_errors": [],
|
| 12 |
+
"correct": false,
|
| 13 |
+
"validation_error": "operands could not be broadcast together with shapes (3,) (26,) "
|
| 14 |
+
},
|
| 15 |
+
"auxiliary": {},
|
| 16 |
+
"auxiliary_descriptions": {},
|
| 17 |
+
"timestamp": 1770405617.0551147,
|
| 18 |
+
"generation": 103
|
| 19 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (6.73 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/edit.diff
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--- a/original.py
|
| 2 |
+
+++ b/original.py
|
| 3 |
+
@@ -1,185 +1,196 @@
|
| 4 |
+
# EVOLVE-BLOCK-START
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
def construct_packing():
|
| 8 |
+
"""
|
| 9 |
+
Constructs an optimized arrangement of 26 circles using Simulated Annealing (SA),
|
| 10 |
+
- enhanced with adaptive radius computation parameters, to find a high-quality packing.
|
| 11 |
+
+ enhanced with adaptive radius computation parameters, an adaptive move strategy,
|
| 12 |
+
+ and a longer cooling schedule to find a high-quality packing.
|
| 13 |
+
|
| 14 |
+
Returns:
|
| 15 |
+
A tuple containing:
|
| 16 |
+
centers: np.array of shape (26, 2) with the final optimized (x, y) coordinates
|
| 17 |
+
radii: np.array of shape (26) with the final radius of each circle
|
| 18 |
+
"""
|
| 19 |
+
n = 26
|
| 20 |
+
|
| 21 |
+
# 1. Initial State and SA Parameters
|
| 22 |
+
# Start with a proven high-quality initial grid configuration (from prior successful programs).
|
| 23 |
+
centers = np.zeros((n, 2))
|
| 24 |
+
d = 0.09525
|
| 25 |
+
grid_coords = np.linspace(d, 1 - d, 5)
|
| 26 |
+
idx = 0
|
| 27 |
+
for x in grid_coords:
|
| 28 |
+
for y in grid_coords:
|
| 29 |
+
centers[idx] = [x, y]
|
| 30 |
+
idx += 1
|
| 31 |
+
- # Perturbation to break symmetry, a key element from successful priors.
|
| 32 |
+
- centers[25] = [0.39, 0.41]
|
| 33 |
+
+ # Perturbation to break symmetry, now with a small random component for diversity.
|
| 34 |
+
+ centers[25] = [0.39 + np.random.uniform(-0.01, 0.01), 0.41 + np.random.uniform(-0.01, 0.01)]
|
| 35 |
+
|
| 36 |
+
- # SA Parameters - tuned for thorough search.
|
| 37 |
+
+ # SA Parameters - tuned for a longer, more thorough search (from a high-performing prior).
|
| 38 |
+
T_initial = 0.01 # Initial temperature (allows large uphill moves)
|
| 39 |
+
T_final = 1e-7 # Final temperature (near deterministic local search)
|
| 40 |
+
- alpha = 0.9998 # Cooling rate (slow, for deeper exploration)
|
| 41 |
+
- max_steps = 150000 # Increased steps for more thorough annealing.
|
| 42 |
+
+ alpha = 0.99985 # Slower cooling rate for more steps
|
| 43 |
+
+ max_steps = 200000 # Increased steps for a deeper search
|
| 44 |
+
|
| 45 |
+
# Move generation parameters
|
| 46 |
+
initial_move_dist = 0.08 # Max move distance at T_initial
|
| 47 |
+
- num_circles_to_move = 3 # Number of circles to perturb each step
|
| 48 |
+
+ # num_circles_to_move will now be adaptive, based on temperature.
|
| 49 |
+
|
| 50 |
+
current_centers = np.copy(centers)
|
| 51 |
+
best_centers = np.copy(centers)
|
| 52 |
+
|
| 53 |
+
# Pre-compute schedules for adaptive quick_solve parameters, inspired by Coordinate Ascent.
|
| 54 |
+
# These will provide smoother transitions for evaluation accuracy.
|
| 55 |
+
quick_iter_schedule = np.linspace(80, 250, max_steps, dtype=int)
|
| 56 |
+
quick_update_schedule = np.linspace(0.75, 0.55, max_steps)
|
| 57 |
+
|
| 58 |
+
# Initial energy calculation (Energy = -Sum of Radii)
|
| 59 |
+
# Use the adaptive compute_max_radii, but with constant factor for initial quick evaluation.
|
| 60 |
+
initial_quick_solve_iter = quick_iter_schedule[0]
|
| 61 |
+
initial_quick_solve_uf = quick_update_schedule[0]
|
| 62 |
+
current_radii = compute_max_radii(current_centers, iterations=initial_quick_solve_iter,
|
| 63 |
+
update_factor=(initial_quick_solve_uf, initial_quick_solve_uf, 0.0))
|
| 64 |
+
current_energy = -np.sum(current_radii)
|
| 65 |
+
best_energy = current_energy
|
| 66 |
+
|
| 67 |
+
T = T_initial
|
| 68 |
+
step = 0
|
| 69 |
+
|
| 70 |
+
# 2. Annealing Loop
|
| 71 |
+
while T > T_final and step < max_steps:
|
| 72 |
+
# Generate a new candidate configuration
|
| 73 |
+
candidate_centers = np.copy(current_centers)
|
| 74 |
+
|
| 75 |
+
# Anneal the move distance based on temperature for adaptive step size.
|
| 76 |
+
move_dist = initial_move_dist * (T / T_initial)**0.5
|
| 77 |
+
+
|
| 78 |
+
+ # Adaptive number of circles to move (crossover from another SA program)
|
| 79 |
+
+ if T > T_initial * 0.1:
|
| 80 |
+
+ num_circles_to_move = 4
|
| 81 |
+
+ elif T > T_initial * 0.01:
|
| 82 |
+
+ num_circles_to_move = 3
|
| 83 |
+
+ elif T > T_initial * 0.001:
|
| 84 |
+
+ num_circles_to_move = 2
|
| 85 |
+
+ else:
|
| 86 |
+
+ num_circles_to_move = 1
|
| 87 |
+
|
| 88 |
+
# Randomly pick a few circles to perturb for diverse exploration.
|
| 89 |
+
circles_to_move_indices = np.random.choice(n, num_circles_to_move, replace=False)
|
| 90 |
+
|
| 91 |
+
# Create random move vectors and apply them.
|
| 92 |
+
move_vectors = np.random.uniform(-move_dist, move_dist, size=(num_circles_to_move, 2))
|
| 93 |
+
candidate_centers[circles_to_move_indices] += move_vectors
|
| 94 |
+
|
| 95 |
+
# Clip all centers to ensure they stay within the unit square.
|
| 96 |
+
candidate_centers = np.clip(candidate_centers, 0.0, 1.0)
|
| 97 |
+
|
| 98 |
+
# Determine current quick_solve parameters from schedules for adaptive evaluation.
|
| 99 |
+
current_quick_solve_iter = quick_iter_schedule[step]
|
| 100 |
+
current_quick_solve_uf = quick_update_schedule[step]
|
| 101 |
+
|
| 102 |
+
# Calculate the energy of the new candidate using adaptive solver.
|
| 103 |
+
candidate_radii = compute_max_radii(candidate_centers, iterations=current_quick_solve_iter,
|
| 104 |
+
update_factor=(current_quick_solve_uf, current_quick_solve_uf, 0.0))
|
| 105 |
+
candidate_energy = -np.sum(candidate_radii)
|
| 106 |
+
|
| 107 |
+
# Metropolis-Hastings acceptance criterion: allows "uphill" moves to escape local optima.
|
| 108 |
+
delta_E = candidate_energy - current_energy
|
| 109 |
+
if delta_E < 0 or np.random.rand() < np.exp(-delta_E / T):
|
| 110 |
+
current_centers = candidate_centers
|
| 111 |
+
current_energy = candidate_energy
|
| 112 |
+
|
| 113 |
+
# Update the best-ever found solution.
|
| 114 |
+
if current_energy < best_energy:
|
| 115 |
+
best_energy = current_energy
|
| 116 |
+
best_centers = np.copy(current_centers)
|
| 117 |
+
|
| 118 |
+
# Cool down the temperature.
|
| 119 |
+
T *= alpha
|
| 120 |
+
step += 1
|
| 121 |
+
|
| 122 |
+
# 3. Finalization
|
| 123 |
+
# Run a final high-precision radius calculation on the best configuration found.
|
| 124 |
+
# Uses a sophisticated adaptive damping schedule for maximum accuracy and stability.
|
| 125 |
+
final_radii = compute_max_radii(best_centers, iterations=10000, update_factor=(0.65, 0.45, 0.25))
|
| 126 |
+
|
| 127 |
+
return best_centers, final_radii
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def compute_max_radii(centers, iterations=5000, update_factor=(0.6, 0.6, 0.0)):
|
| 131 |
+
"""
|
| 132 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 133 |
+
This version supports an adaptive damping schedule for the update factor.
|
| 134 |
+
|
| 135 |
+
Args:
|
| 136 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates.
|
| 137 |
+
iterations: The number of relaxation iterations to perform.
|
| 138 |
+
update_factor: Can be:
|
| 139 |
+
- A single float: constant damping factor.
|
| 140 |
+
- A tuple (initial_uf, final_uf, switch_ratio): adaptive damping.
|
| 141 |
+
`initial_uf` is used at the start, decaying linearly to `final_uf`
|
| 142 |
+
over `iterations * switch_ratio` steps, then `final_uf` for the rest.
|
| 143 |
+
|
| 144 |
+
Returns:
|
| 145 |
+
np.array of shape (n) with the radius of each circle.
|
| 146 |
+
"""
|
| 147 |
+
n = centers.shape[0]
|
| 148 |
+
# Small non-zero start to prevent issues on the first iteration.
|
| 149 |
+
radii = np.full(n, 1e-6)
|
| 150 |
+
|
| 151 |
+
# Pre-calculate distances between centers.
|
| 152 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 153 |
+
# Set diagonal to infinity so a circle isn't constrained by itself.
|
| 154 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 155 |
+
|
| 156 |
+
# Pre-calculate wall distances for all circles.
|
| 157 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 158 |
+
|
| 159 |
+
# Interpret update_factor parameter for adaptive damping.
|
| 160 |
+
if isinstance(update_factor, (float, int)):
|
| 161 |
+
initial_uf = float(update_factor)
|
| 162 |
+
final_uf = float(update_factor)
|
| 163 |
+
switch_ratio = 0.0 # No adaptive schedule if a single float is provided
|
| 164 |
+
else: # Expecting a tuple (initial_uf, final_uf, switch_ratio)
|
| 165 |
+
initial_uf, final_uf, switch_ratio = update_factor
|
| 166 |
+
|
| 167 |
+
for k in range(iterations):
|
| 168 |
+
radii_old = np.copy(radii)
|
| 169 |
+
|
| 170 |
+
# Determine current damping factor based on iteration progress and schedule.
|
| 171 |
+
current_uf = initial_uf
|
| 172 |
+
if switch_ratio > 0 and k < iterations * switch_ratio:
|
| 173 |
+
# Linear interpolation from initial_uf to final_uf over the switch_ratio duration
|
| 174 |
+
current_uf = initial_uf + (final_uf - initial_uf) * (k / (iterations * switch_ratio))
|
| 175 |
+
elif switch_ratio > 0 and k >= iterations * switch_ratio:
|
| 176 |
+
# After the switch point, use the final damping factor
|
| 177 |
+
current_uf = final_uf
|
| 178 |
+
# If switch_ratio is 0, current_uf remains initial_uf as initialized above.
|
| 179 |
+
|
| 180 |
+
# Calculate all potential radii based on other circles in a vectorized way.
|
| 181 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 182 |
+
|
| 183 |
+
# Combine with wall constraints.
|
| 184 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 185 |
+
|
| 186 |
+
# New radii for this iteration (must be non-negative).
|
| 187 |
+
new_r = np.maximum(0.0, potential_r)
|
| 188 |
+
|
| 189 |
+
# Dampen the update to prevent oscillations.
|
| 190 |
+
radii = radii_old + current_uf * (new_r - radii_old)
|
| 191 |
+
|
| 192 |
+
# Early exit condition if radii have converged.
|
| 193 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 194 |
+
break
|
| 195 |
+
|
| 196 |
+
return radii
|
| 197 |
+
# EVOLVE-BLOCK-END
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
# This part remains fixed (not evolved)
|
| 201 |
+
def run_packing():
|
| 202 |
+
"""Run the circle packing constructor for n=26"""
|
| 203 |
+
centers, radii = construct_packing()
|
| 204 |
+
# Calculate the sum of radii
|
| 205 |
+
sum_radii = np.sum(radii)
|
| 206 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/main.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
def construct_packing():
|
| 5 |
+
"""
|
| 6 |
+
Constructs an optimized arrangement of 26 circles using Simulated Annealing (SA),
|
| 7 |
+
enhanced with adaptive radius computation parameters, an adaptive move strategy,
|
| 8 |
+
and a longer cooling schedule to find a high-quality packing.
|
| 9 |
+
|
| 10 |
+
Returns:
|
| 11 |
+
A tuple containing:
|
| 12 |
+
centers: np.array of shape (26, 2) with the final optimized (x, y) coordinates
|
| 13 |
+
radii: np.array of shape (26) with the final radius of each circle
|
| 14 |
+
"""
|
| 15 |
+
n = 26
|
| 16 |
+
|
| 17 |
+
# 1. Initial State and SA Parameters
|
| 18 |
+
# Start with a proven high-quality initial grid configuration (from prior successful programs).
|
| 19 |
+
centers = np.zeros((n, 2))
|
| 20 |
+
d = 0.09525
|
| 21 |
+
grid_coords = np.linspace(d, 1 - d, 5)
|
| 22 |
+
idx = 0
|
| 23 |
+
for x in grid_coords:
|
| 24 |
+
for y in grid_coords:
|
| 25 |
+
centers[idx] = [x, y]
|
| 26 |
+
idx += 1
|
| 27 |
+
# Perturbation to break symmetry, now with a small random component for diversity.
|
| 28 |
+
centers[25] = [0.39 + np.random.uniform(-0.01, 0.01), 0.41 + np.random.uniform(-0.01, 0.01)]
|
| 29 |
+
|
| 30 |
+
# SA Parameters - tuned for a longer, more thorough search (from a high-performing prior).
|
| 31 |
+
T_initial = 0.01 # Initial temperature (allows large uphill moves)
|
| 32 |
+
T_final = 1e-7 # Final temperature (near deterministic local search)
|
| 33 |
+
alpha = 0.99985 # Slower cooling rate for more steps
|
| 34 |
+
max_steps = 200000 # Increased steps for a deeper search
|
| 35 |
+
|
| 36 |
+
# Move generation parameters
|
| 37 |
+
initial_move_dist = 0.08 # Max move distance at T_initial
|
| 38 |
+
# num_circles_to_move will now be adaptive, based on temperature.
|
| 39 |
+
|
| 40 |
+
current_centers = np.copy(centers)
|
| 41 |
+
best_centers = np.copy(centers)
|
| 42 |
+
|
| 43 |
+
# Pre-compute schedules for adaptive quick_solve parameters, inspired by Coordinate Ascent.
|
| 44 |
+
# These will provide smoother transitions for evaluation accuracy.
|
| 45 |
+
quick_iter_schedule = np.linspace(80, 250, max_steps, dtype=int)
|
| 46 |
+
quick_update_schedule = np.linspace(0.75, 0.55, max_steps)
|
| 47 |
+
|
| 48 |
+
# Initial energy calculation (Energy = -Sum of Radii)
|
| 49 |
+
# Use the adaptive compute_max_radii, but with constant factor for initial quick evaluation.
|
| 50 |
+
initial_quick_solve_iter = quick_iter_schedule[0]
|
| 51 |
+
initial_quick_solve_uf = quick_update_schedule[0]
|
| 52 |
+
current_radii = compute_max_radii(current_centers, iterations=initial_quick_solve_iter,
|
| 53 |
+
update_factor=(initial_quick_solve_uf, initial_quick_solve_uf, 0.0))
|
| 54 |
+
current_energy = -np.sum(current_radii)
|
| 55 |
+
best_energy = current_energy
|
| 56 |
+
|
| 57 |
+
T = T_initial
|
| 58 |
+
step = 0
|
| 59 |
+
|
| 60 |
+
# 2. Annealing Loop
|
| 61 |
+
while T > T_final and step < max_steps:
|
| 62 |
+
# Generate a new candidate configuration
|
| 63 |
+
candidate_centers = np.copy(current_centers)
|
| 64 |
+
|
| 65 |
+
# Anneal the move distance based on temperature for adaptive step size.
|
| 66 |
+
move_dist = initial_move_dist * (T / T_initial)**0.5
|
| 67 |
+
|
| 68 |
+
# Adaptive number of circles to move (crossover from another SA program)
|
| 69 |
+
if T > T_initial * 0.1:
|
| 70 |
+
num_circles_to_move = 4
|
| 71 |
+
elif T > T_initial * 0.01:
|
| 72 |
+
num_circles_to_move = 3
|
| 73 |
+
elif T > T_initial * 0.001:
|
| 74 |
+
num_circles_to_move = 2
|
| 75 |
+
else:
|
| 76 |
+
num_circles_to_move = 1
|
| 77 |
+
|
| 78 |
+
# Randomly pick a few circles to perturb for diverse exploration.
|
| 79 |
+
circles_to_move_indices = np.random.choice(n, num_circles_to_move, replace=False)
|
| 80 |
+
|
| 81 |
+
# Create random move vectors and apply them.
|
| 82 |
+
move_vectors = np.random.uniform(-move_dist, move_dist, size=(num_circles_to_move, 2))
|
| 83 |
+
candidate_centers[circles_to_move_indices] += move_vectors
|
| 84 |
+
|
| 85 |
+
# Clip all centers to ensure they stay within the unit square.
|
| 86 |
+
candidate_centers = np.clip(candidate_centers, 0.0, 1.0)
|
| 87 |
+
|
| 88 |
+
# Determine current quick_solve parameters from schedules for adaptive evaluation.
|
| 89 |
+
current_quick_solve_iter = quick_iter_schedule[step]
|
| 90 |
+
current_quick_solve_uf = quick_update_schedule[step]
|
| 91 |
+
|
| 92 |
+
# Calculate the energy of the new candidate using adaptive solver.
|
| 93 |
+
candidate_radii = compute_max_radii(candidate_centers, iterations=current_quick_solve_iter,
|
| 94 |
+
update_factor=(current_quick_solve_uf, current_quick_solve_uf, 0.0))
|
| 95 |
+
candidate_energy = -np.sum(candidate_radii)
|
| 96 |
+
|
| 97 |
+
# Metropolis-Hastings acceptance criterion: allows "uphill" moves to escape local optima.
|
| 98 |
+
delta_E = candidate_energy - current_energy
|
| 99 |
+
if delta_E < 0 or np.random.rand() < np.exp(-delta_E / T):
|
| 100 |
+
current_centers = candidate_centers
|
| 101 |
+
current_energy = candidate_energy
|
| 102 |
+
|
| 103 |
+
# Update the best-ever found solution.
|
| 104 |
+
if current_energy < best_energy:
|
| 105 |
+
best_energy = current_energy
|
| 106 |
+
best_centers = np.copy(current_centers)
|
| 107 |
+
|
| 108 |
+
# Cool down the temperature.
|
| 109 |
+
T *= alpha
|
| 110 |
+
step += 1
|
| 111 |
+
|
| 112 |
+
# 3. Finalization
|
| 113 |
+
# Run a final high-precision radius calculation on the best configuration found.
|
| 114 |
+
# Uses a sophisticated adaptive damping schedule for maximum accuracy and stability.
|
| 115 |
+
final_radii = compute_max_radii(best_centers, iterations=10000, update_factor=(0.65, 0.45, 0.25))
|
| 116 |
+
|
| 117 |
+
return best_centers, final_radii
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def compute_max_radii(centers, iterations=5000, update_factor=(0.6, 0.6, 0.0)):
|
| 121 |
+
"""
|
| 122 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 123 |
+
This version supports an adaptive damping schedule for the update factor.
|
| 124 |
+
|
| 125 |
+
Args:
|
| 126 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates.
|
| 127 |
+
iterations: The number of relaxation iterations to perform.
|
| 128 |
+
update_factor: Can be:
|
| 129 |
+
- A single float: constant damping factor.
|
| 130 |
+
- A tuple (initial_uf, final_uf, switch_ratio): adaptive damping.
|
| 131 |
+
`initial_uf` is used at the start, decaying linearly to `final_uf`
|
| 132 |
+
over `iterations * switch_ratio` steps, then `final_uf` for the rest.
|
| 133 |
+
|
| 134 |
+
Returns:
|
| 135 |
+
np.array of shape (n) with the radius of each circle.
|
| 136 |
+
"""
|
| 137 |
+
n = centers.shape[0]
|
| 138 |
+
# Small non-zero start to prevent issues on the first iteration.
|
| 139 |
+
radii = np.full(n, 1e-6)
|
| 140 |
+
|
| 141 |
+
# Pre-calculate distances between centers.
|
| 142 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 143 |
+
# Set diagonal to infinity so a circle isn't constrained by itself.
|
| 144 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 145 |
+
|
| 146 |
+
# Pre-calculate wall distances for all circles.
|
| 147 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 148 |
+
|
| 149 |
+
# Interpret update_factor parameter for adaptive damping.
|
| 150 |
+
if isinstance(update_factor, (float, int)):
|
| 151 |
+
initial_uf = float(update_factor)
|
| 152 |
+
final_uf = float(update_factor)
|
| 153 |
+
switch_ratio = 0.0 # No adaptive schedule if a single float is provided
|
| 154 |
+
else: # Expecting a tuple (initial_uf, final_uf, switch_ratio)
|
| 155 |
+
initial_uf, final_uf, switch_ratio = update_factor
|
| 156 |
+
|
| 157 |
+
for k in range(iterations):
|
| 158 |
+
radii_old = np.copy(radii)
|
| 159 |
+
|
| 160 |
+
# Determine current damping factor based on iteration progress and schedule.
|
| 161 |
+
current_uf = initial_uf
|
| 162 |
+
if switch_ratio > 0 and k < iterations * switch_ratio:
|
| 163 |
+
# Linear interpolation from initial_uf to final_uf over the switch_ratio duration
|
| 164 |
+
current_uf = initial_uf + (final_uf - initial_uf) * (k / (iterations * switch_ratio))
|
| 165 |
+
elif switch_ratio > 0 and k >= iterations * switch_ratio:
|
| 166 |
+
# After the switch point, use the final damping factor
|
| 167 |
+
current_uf = final_uf
|
| 168 |
+
# If switch_ratio is 0, current_uf remains initial_uf as initialized above.
|
| 169 |
+
|
| 170 |
+
# Calculate all potential radii based on other circles in a vectorized way.
|
| 171 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 172 |
+
|
| 173 |
+
# Combine with wall constraints.
|
| 174 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 175 |
+
|
| 176 |
+
# New radii for this iteration (must be non-negative).
|
| 177 |
+
new_r = np.maximum(0.0, potential_r)
|
| 178 |
+
|
| 179 |
+
# Dampen the update to prevent oscillations.
|
| 180 |
+
radii = radii_old + current_uf * (new_r - radii_old)
|
| 181 |
+
|
| 182 |
+
# Early exit condition if radii have converged.
|
| 183 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 184 |
+
break
|
| 185 |
+
|
| 186 |
+
return radii
|
| 187 |
+
# EVOLVE-BLOCK-END
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
# This part remains fixed (not evolved)
|
| 191 |
+
def run_packing():
|
| 192 |
+
"""Run the circle packing constructor for n=26"""
|
| 193 |
+
centers, radii = construct_packing()
|
| 194 |
+
# Calculate the sum of radii
|
| 195 |
+
sum_radii = np.sum(radii)
|
| 196 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/original.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
def construct_packing():
|
| 5 |
+
"""
|
| 6 |
+
Constructs an optimized arrangement of 26 circles using Simulated Annealing (SA),
|
| 7 |
+
enhanced with adaptive radius computation parameters, to find a high-quality packing.
|
| 8 |
+
|
| 9 |
+
Returns:
|
| 10 |
+
A tuple containing:
|
| 11 |
+
centers: np.array of shape (26, 2) with the final optimized (x, y) coordinates
|
| 12 |
+
radii: np.array of shape (26) with the final radius of each circle
|
| 13 |
+
"""
|
| 14 |
+
n = 26
|
| 15 |
+
|
| 16 |
+
# 1. Initial State and SA Parameters
|
| 17 |
+
# Start with a proven high-quality initial grid configuration (from prior successful programs).
|
| 18 |
+
centers = np.zeros((n, 2))
|
| 19 |
+
d = 0.09525
|
| 20 |
+
grid_coords = np.linspace(d, 1 - d, 5)
|
| 21 |
+
idx = 0
|
| 22 |
+
for x in grid_coords:
|
| 23 |
+
for y in grid_coords:
|
| 24 |
+
centers[idx] = [x, y]
|
| 25 |
+
idx += 1
|
| 26 |
+
# Perturbation to break symmetry, a key element from successful priors.
|
| 27 |
+
centers[25] = [0.39, 0.41]
|
| 28 |
+
|
| 29 |
+
# SA Parameters - tuned for thorough search.
|
| 30 |
+
T_initial = 0.01 # Initial temperature (allows large uphill moves)
|
| 31 |
+
T_final = 1e-7 # Final temperature (near deterministic local search)
|
| 32 |
+
alpha = 0.9998 # Cooling rate (slow, for deeper exploration)
|
| 33 |
+
max_steps = 150000 # Increased steps for more thorough annealing.
|
| 34 |
+
|
| 35 |
+
# Move generation parameters
|
| 36 |
+
initial_move_dist = 0.08 # Max move distance at T_initial
|
| 37 |
+
num_circles_to_move = 3 # Number of circles to perturb each step
|
| 38 |
+
|
| 39 |
+
current_centers = np.copy(centers)
|
| 40 |
+
best_centers = np.copy(centers)
|
| 41 |
+
|
| 42 |
+
# Pre-compute schedules for adaptive quick_solve parameters, inspired by Coordinate Ascent.
|
| 43 |
+
# These will provide smoother transitions for evaluation accuracy.
|
| 44 |
+
quick_iter_schedule = np.linspace(80, 250, max_steps, dtype=int)
|
| 45 |
+
quick_update_schedule = np.linspace(0.75, 0.55, max_steps)
|
| 46 |
+
|
| 47 |
+
# Initial energy calculation (Energy = -Sum of Radii)
|
| 48 |
+
# Use the adaptive compute_max_radii, but with constant factor for initial quick evaluation.
|
| 49 |
+
initial_quick_solve_iter = quick_iter_schedule[0]
|
| 50 |
+
initial_quick_solve_uf = quick_update_schedule[0]
|
| 51 |
+
current_radii = compute_max_radii(current_centers, iterations=initial_quick_solve_iter,
|
| 52 |
+
update_factor=(initial_quick_solve_uf, initial_quick_solve_uf, 0.0))
|
| 53 |
+
current_energy = -np.sum(current_radii)
|
| 54 |
+
best_energy = current_energy
|
| 55 |
+
|
| 56 |
+
T = T_initial
|
| 57 |
+
step = 0
|
| 58 |
+
|
| 59 |
+
# 2. Annealing Loop
|
| 60 |
+
while T > T_final and step < max_steps:
|
| 61 |
+
# Generate a new candidate configuration
|
| 62 |
+
candidate_centers = np.copy(current_centers)
|
| 63 |
+
|
| 64 |
+
# Anneal the move distance based on temperature for adaptive step size.
|
| 65 |
+
move_dist = initial_move_dist * (T / T_initial)**0.5
|
| 66 |
+
|
| 67 |
+
# Randomly pick a few circles to perturb for diverse exploration.
|
| 68 |
+
circles_to_move_indices = np.random.choice(n, num_circles_to_move, replace=False)
|
| 69 |
+
|
| 70 |
+
# Create random move vectors and apply them.
|
| 71 |
+
move_vectors = np.random.uniform(-move_dist, move_dist, size=(num_circles_to_move, 2))
|
| 72 |
+
candidate_centers[circles_to_move_indices] += move_vectors
|
| 73 |
+
|
| 74 |
+
# Clip all centers to ensure they stay within the unit square.
|
| 75 |
+
candidate_centers = np.clip(candidate_centers, 0.0, 1.0)
|
| 76 |
+
|
| 77 |
+
# Determine current quick_solve parameters from schedules for adaptive evaluation.
|
| 78 |
+
current_quick_solve_iter = quick_iter_schedule[step]
|
| 79 |
+
current_quick_solve_uf = quick_update_schedule[step]
|
| 80 |
+
|
| 81 |
+
# Calculate the energy of the new candidate using adaptive solver.
|
| 82 |
+
candidate_radii = compute_max_radii(candidate_centers, iterations=current_quick_solve_iter,
|
| 83 |
+
update_factor=(current_quick_solve_uf, current_quick_solve_uf, 0.0))
|
| 84 |
+
candidate_energy = -np.sum(candidate_radii)
|
| 85 |
+
|
| 86 |
+
# Metropolis-Hastings acceptance criterion: allows "uphill" moves to escape local optima.
|
| 87 |
+
delta_E = candidate_energy - current_energy
|
| 88 |
+
if delta_E < 0 or np.random.rand() < np.exp(-delta_E / T):
|
| 89 |
+
current_centers = candidate_centers
|
| 90 |
+
current_energy = candidate_energy
|
| 91 |
+
|
| 92 |
+
# Update the best-ever found solution.
|
| 93 |
+
if current_energy < best_energy:
|
| 94 |
+
best_energy = current_energy
|
| 95 |
+
best_centers = np.copy(current_centers)
|
| 96 |
+
|
| 97 |
+
# Cool down the temperature.
|
| 98 |
+
T *= alpha
|
| 99 |
+
step += 1
|
| 100 |
+
|
| 101 |
+
# 3. Finalization
|
| 102 |
+
# Run a final high-precision radius calculation on the best configuration found.
|
| 103 |
+
# Uses a sophisticated adaptive damping schedule for maximum accuracy and stability.
|
| 104 |
+
final_radii = compute_max_radii(best_centers, iterations=10000, update_factor=(0.65, 0.45, 0.25))
|
| 105 |
+
|
| 106 |
+
return best_centers, final_radii
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def compute_max_radii(centers, iterations=5000, update_factor=(0.6, 0.6, 0.0)):
|
| 110 |
+
"""
|
| 111 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 112 |
+
This version supports an adaptive damping schedule for the update factor.
|
| 113 |
+
|
| 114 |
+
Args:
|
| 115 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates.
|
| 116 |
+
iterations: The number of relaxation iterations to perform.
|
| 117 |
+
update_factor: Can be:
|
| 118 |
+
- A single float: constant damping factor.
|
| 119 |
+
- A tuple (initial_uf, final_uf, switch_ratio): adaptive damping.
|
| 120 |
+
`initial_uf` is used at the start, decaying linearly to `final_uf`
|
| 121 |
+
over `iterations * switch_ratio` steps, then `final_uf` for the rest.
|
| 122 |
+
|
| 123 |
+
Returns:
|
| 124 |
+
np.array of shape (n) with the radius of each circle.
|
| 125 |
+
"""
|
| 126 |
+
n = centers.shape[0]
|
| 127 |
+
# Small non-zero start to prevent issues on the first iteration.
|
| 128 |
+
radii = np.full(n, 1e-6)
|
| 129 |
+
|
| 130 |
+
# Pre-calculate distances between centers.
|
| 131 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 132 |
+
# Set diagonal to infinity so a circle isn't constrained by itself.
|
| 133 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 134 |
+
|
| 135 |
+
# Pre-calculate wall distances for all circles.
|
| 136 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 137 |
+
|
| 138 |
+
# Interpret update_factor parameter for adaptive damping.
|
| 139 |
+
if isinstance(update_factor, (float, int)):
|
| 140 |
+
initial_uf = float(update_factor)
|
| 141 |
+
final_uf = float(update_factor)
|
| 142 |
+
switch_ratio = 0.0 # No adaptive schedule if a single float is provided
|
| 143 |
+
else: # Expecting a tuple (initial_uf, final_uf, switch_ratio)
|
| 144 |
+
initial_uf, final_uf, switch_ratio = update_factor
|
| 145 |
+
|
| 146 |
+
for k in range(iterations):
|
| 147 |
+
radii_old = np.copy(radii)
|
| 148 |
+
|
| 149 |
+
# Determine current damping factor based on iteration progress and schedule.
|
| 150 |
+
current_uf = initial_uf
|
| 151 |
+
if switch_ratio > 0 and k < iterations * switch_ratio:
|
| 152 |
+
# Linear interpolation from initial_uf to final_uf over the switch_ratio duration
|
| 153 |
+
current_uf = initial_uf + (final_uf - initial_uf) * (k / (iterations * switch_ratio))
|
| 154 |
+
elif switch_ratio > 0 and k >= iterations * switch_ratio:
|
| 155 |
+
# After the switch point, use the final damping factor
|
| 156 |
+
current_uf = final_uf
|
| 157 |
+
# If switch_ratio is 0, current_uf remains initial_uf as initialized above.
|
| 158 |
+
|
| 159 |
+
# Calculate all potential radii based on other circles in a vectorized way.
|
| 160 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 161 |
+
|
| 162 |
+
# Combine with wall constraints.
|
| 163 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 164 |
+
|
| 165 |
+
# New radii for this iteration (must be non-negative).
|
| 166 |
+
new_r = np.maximum(0.0, potential_r)
|
| 167 |
+
|
| 168 |
+
# Dampen the update to prevent oscillations.
|
| 169 |
+
radii = radii_old + current_uf * (new_r - radii_old)
|
| 170 |
+
|
| 171 |
+
# Early exit condition if radii have converged.
|
| 172 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 173 |
+
break
|
| 174 |
+
|
| 175 |
+
return radii
|
| 176 |
+
# EVOLVE-BLOCK-END
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
# This part remains fixed (not evolved)
|
| 180 |
+
def run_packing():
|
| 181 |
+
"""Run the circle packing constructor for n=26"""
|
| 182 |
+
centers, radii = construct_packing()
|
| 183 |
+
# Calculate the sum of radii
|
| 184 |
+
sum_radii = np.sum(radii)
|
| 185 |
+
return centers, radii, sum_radii
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/results/metrics.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 2.5368049435910565,
|
| 3 |
+
"correct": true,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 2.5368049435910565,
|
| 6 |
+
"public": {
|
| 7 |
+
"centers_str": " centers[0] = (0.1099, 0.1236)\n centers[1] = (0.0707, 0.3000)\n centers[2] = (0.1128, 0.4787)\n centers[3] = (0.0988, 0.6938)\n centers[4] = (0.1037, 0.8962)\n centers[5] = (0.3104, 0.0928)\n centers[6] = (0.2609, 0.2991)\n centers[7] = (0.2872, 0.5195)\n centers[8] = (0.2879, 0.6832)\n centers[9] = (0.3208, 0.8852)\n centers[10] = (0.4911, 0.0880)\n centers[11] = (0.4930, 0.2886)\n centers[12] = (0.5326, 0.5010)\n centers[13] = (0.4947, 0.7079)\n centers[14] = (0.5188, 0.9104)\n centers[15] = (0.7014, 0.1256)\n centers[16] = (0.6840, 0.3343)\n centers[17] = (0.7089, 0.4999)\n centers[18] = (0.7335, 0.7044)\n centers[19] = (0.6901, 0.9144)\n centers[20] = (0.9125, 0.0874)\n centers[21] = (0.8815, 0.2909)\n centers[22] = (0.8961, 0.5141)\n centers[23] = (0.9270, 0.6884)\n centers[24] = (0.8865, 0.8823)\n centers[25] = (0.3886, 0.4340)",
|
| 8 |
+
"num_circles": 26
|
| 9 |
+
},
|
| 10 |
+
"private": {
|
| 11 |
+
"reported_sum_of_radii": 2.5368049435910565
|
| 12 |
+
},
|
| 13 |
+
"execution_time_mean": 43.77116736583412,
|
| 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": 1770405697.9377902,
|
| 24 |
+
"generation": 104
|
| 25 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_104/rewrite.txt
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EVOLVE-BLOCK-START
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
def construct_packing():
|
| 5 |
+
"""
|
| 6 |
+
Constructs an optimized arrangement of 26 circles using Simulated Annealing (SA),
|
| 7 |
+
enhanced with adaptive radius computation parameters, an adaptive move strategy,
|
| 8 |
+
and a longer cooling schedule to find a high-quality packing.
|
| 9 |
+
|
| 10 |
+
Returns:
|
| 11 |
+
A tuple containing:
|
| 12 |
+
centers: np.array of shape (26, 2) with the final optimized (x, y) coordinates
|
| 13 |
+
radii: np.array of shape (26) with the final radius of each circle
|
| 14 |
+
"""
|
| 15 |
+
n = 26
|
| 16 |
+
|
| 17 |
+
# 1. Initial State and SA Parameters
|
| 18 |
+
# Start with a proven high-quality initial grid configuration (from prior successful programs).
|
| 19 |
+
centers = np.zeros((n, 2))
|
| 20 |
+
d = 0.09525
|
| 21 |
+
grid_coords = np.linspace(d, 1 - d, 5)
|
| 22 |
+
idx = 0
|
| 23 |
+
for x in grid_coords:
|
| 24 |
+
for y in grid_coords:
|
| 25 |
+
centers[idx] = [x, y]
|
| 26 |
+
idx += 1
|
| 27 |
+
# Perturbation to break symmetry, now with a small random component for diversity.
|
| 28 |
+
centers[25] = [0.39 + np.random.uniform(-0.01, 0.01), 0.41 + np.random.uniform(-0.01, 0.01)]
|
| 29 |
+
|
| 30 |
+
# SA Parameters - tuned for a longer, more thorough search (from a high-performing prior).
|
| 31 |
+
T_initial = 0.01 # Initial temperature (allows large uphill moves)
|
| 32 |
+
T_final = 1e-7 # Final temperature (near deterministic local search)
|
| 33 |
+
alpha = 0.99985 # Slower cooling rate for more steps
|
| 34 |
+
max_steps = 200000 # Increased steps for a deeper search
|
| 35 |
+
|
| 36 |
+
# Move generation parameters
|
| 37 |
+
initial_move_dist = 0.08 # Max move distance at T_initial
|
| 38 |
+
# num_circles_to_move will now be adaptive, based on temperature.
|
| 39 |
+
|
| 40 |
+
current_centers = np.copy(centers)
|
| 41 |
+
best_centers = np.copy(centers)
|
| 42 |
+
|
| 43 |
+
# Pre-compute schedules for adaptive quick_solve parameters, inspired by Coordinate Ascent.
|
| 44 |
+
# These will provide smoother transitions for evaluation accuracy.
|
| 45 |
+
quick_iter_schedule = np.linspace(80, 250, max_steps, dtype=int)
|
| 46 |
+
quick_update_schedule = np.linspace(0.75, 0.55, max_steps)
|
| 47 |
+
|
| 48 |
+
# Initial energy calculation (Energy = -Sum of Radii)
|
| 49 |
+
# Use the adaptive compute_max_radii, but with constant factor for initial quick evaluation.
|
| 50 |
+
initial_quick_solve_iter = quick_iter_schedule[0]
|
| 51 |
+
initial_quick_solve_uf = quick_update_schedule[0]
|
| 52 |
+
current_radii = compute_max_radii(current_centers, iterations=initial_quick_solve_iter,
|
| 53 |
+
update_factor=(initial_quick_solve_uf, initial_quick_solve_uf, 0.0))
|
| 54 |
+
current_energy = -np.sum(current_radii)
|
| 55 |
+
best_energy = current_energy
|
| 56 |
+
|
| 57 |
+
T = T_initial
|
| 58 |
+
step = 0
|
| 59 |
+
|
| 60 |
+
# 2. Annealing Loop
|
| 61 |
+
while T > T_final and step < max_steps:
|
| 62 |
+
# Generate a new candidate configuration
|
| 63 |
+
candidate_centers = np.copy(current_centers)
|
| 64 |
+
|
| 65 |
+
# Anneal the move distance based on temperature for adaptive step size.
|
| 66 |
+
move_dist = initial_move_dist * (T / T_initial)**0.5
|
| 67 |
+
|
| 68 |
+
# Adaptive number of circles to move (crossover from another SA program)
|
| 69 |
+
if T > T_initial * 0.1:
|
| 70 |
+
num_circles_to_move = 4
|
| 71 |
+
elif T > T_initial * 0.01:
|
| 72 |
+
num_circles_to_move = 3
|
| 73 |
+
elif T > T_initial * 0.001:
|
| 74 |
+
num_circles_to_move = 2
|
| 75 |
+
else:
|
| 76 |
+
num_circles_to_move = 1
|
| 77 |
+
|
| 78 |
+
# Randomly pick a few circles to perturb for diverse exploration.
|
| 79 |
+
circles_to_move_indices = np.random.choice(n, num_circles_to_move, replace=False)
|
| 80 |
+
|
| 81 |
+
# Create random move vectors and apply them.
|
| 82 |
+
move_vectors = np.random.uniform(-move_dist, move_dist, size=(num_circles_to_move, 2))
|
| 83 |
+
candidate_centers[circles_to_move_indices] += move_vectors
|
| 84 |
+
|
| 85 |
+
# Clip all centers to ensure they stay within the unit square.
|
| 86 |
+
candidate_centers = np.clip(candidate_centers, 0.0, 1.0)
|
| 87 |
+
|
| 88 |
+
# Determine current quick_solve parameters from schedules for adaptive evaluation.
|
| 89 |
+
current_quick_solve_iter = quick_iter_schedule[step]
|
| 90 |
+
current_quick_solve_uf = quick_update_schedule[step]
|
| 91 |
+
|
| 92 |
+
# Calculate the energy of the new candidate using adaptive solver.
|
| 93 |
+
candidate_radii = compute_max_radii(candidate_centers, iterations=current_quick_solve_iter,
|
| 94 |
+
update_factor=(current_quick_solve_uf, current_quick_solve_uf, 0.0))
|
| 95 |
+
candidate_energy = -np.sum(candidate_radii)
|
| 96 |
+
|
| 97 |
+
# Metropolis-Hastings acceptance criterion: allows "uphill" moves to escape local optima.
|
| 98 |
+
delta_E = candidate_energy - current_energy
|
| 99 |
+
if delta_E < 0 or np.random.rand() < np.exp(-delta_E / T):
|
| 100 |
+
current_centers = candidate_centers
|
| 101 |
+
current_energy = candidate_energy
|
| 102 |
+
|
| 103 |
+
# Update the best-ever found solution.
|
| 104 |
+
if current_energy < best_energy:
|
| 105 |
+
best_energy = current_energy
|
| 106 |
+
best_centers = np.copy(current_centers)
|
| 107 |
+
|
| 108 |
+
# Cool down the temperature.
|
| 109 |
+
T *= alpha
|
| 110 |
+
step += 1
|
| 111 |
+
|
| 112 |
+
# 3. Finalization
|
| 113 |
+
# Run a final high-precision radius calculation on the best configuration found.
|
| 114 |
+
# Uses a sophisticated adaptive damping schedule for maximum accuracy and stability.
|
| 115 |
+
final_radii = compute_max_radii(best_centers, iterations=10000, update_factor=(0.65, 0.45, 0.25))
|
| 116 |
+
|
| 117 |
+
return best_centers, final_radii
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def compute_max_radii(centers, iterations=5000, update_factor=(0.6, 0.6, 0.0)):
|
| 121 |
+
"""
|
| 122 |
+
Computes maximum radii using a fast, vectorized, damped Jacobi-style solver.
|
| 123 |
+
This version supports an adaptive damping schedule for the update factor.
|
| 124 |
+
|
| 125 |
+
Args:
|
| 126 |
+
centers: np.array of shape (n, 2) with (x, y) coordinates.
|
| 127 |
+
iterations: The number of relaxation iterations to perform.
|
| 128 |
+
update_factor: Can be:
|
| 129 |
+
- A single float: constant damping factor.
|
| 130 |
+
- A tuple (initial_uf, final_uf, switch_ratio): adaptive damping.
|
| 131 |
+
`initial_uf` is used at the start, decaying linearly to `final_uf`
|
| 132 |
+
over `iterations * switch_ratio` steps, then `final_uf` for the rest.
|
| 133 |
+
|
| 134 |
+
Returns:
|
| 135 |
+
np.array of shape (n) with the radius of each circle.
|
| 136 |
+
"""
|
| 137 |
+
n = centers.shape[0]
|
| 138 |
+
# Small non-zero start to prevent issues on the first iteration.
|
| 139 |
+
radii = np.full(n, 1e-6)
|
| 140 |
+
|
| 141 |
+
# Pre-calculate distances between centers.
|
| 142 |
+
dist_matrix = np.sqrt(((centers[:, np.newaxis, :] - centers[np.newaxis, :, :])**2).sum(axis=2))
|
| 143 |
+
# Set diagonal to infinity so a circle isn't constrained by itself.
|
| 144 |
+
np.fill_diagonal(dist_matrix, np.inf)
|
| 145 |
+
|
| 146 |
+
# Pre-calculate wall distances for all circles.
|
| 147 |
+
wall_dists = np.min(np.hstack([centers, 1.0 - centers]), axis=1)
|
| 148 |
+
|
| 149 |
+
# Interpret update_factor parameter for adaptive damping.
|
| 150 |
+
if isinstance(update_factor, (float, int)):
|
| 151 |
+
initial_uf = float(update_factor)
|
| 152 |
+
final_uf = float(update_factor)
|
| 153 |
+
switch_ratio = 0.0 # No adaptive schedule if a single float is provided
|
| 154 |
+
else: # Expecting a tuple (initial_uf, final_uf, switch_ratio)
|
| 155 |
+
initial_uf, final_uf, switch_ratio = update_factor
|
| 156 |
+
|
| 157 |
+
for k in range(iterations):
|
| 158 |
+
radii_old = np.copy(radii)
|
| 159 |
+
|
| 160 |
+
# Determine current damping factor based on iteration progress and schedule.
|
| 161 |
+
current_uf = initial_uf
|
| 162 |
+
if switch_ratio > 0 and k < iterations * switch_ratio:
|
| 163 |
+
# Linear interpolation from initial_uf to final_uf over the switch_ratio duration
|
| 164 |
+
current_uf = initial_uf + (final_uf - initial_uf) * (k / (iterations * switch_ratio))
|
| 165 |
+
elif switch_ratio > 0 and k >= iterations * switch_ratio:
|
| 166 |
+
# After the switch point, use the final damping factor
|
| 167 |
+
current_uf = final_uf
|
| 168 |
+
# If switch_ratio is 0, current_uf remains initial_uf as initialized above.
|
| 169 |
+
|
| 170 |
+
# Calculate all potential radii based on other circles in a vectorized way.
|
| 171 |
+
potential_r_circles = np.min(dist_matrix - radii_old, axis=1)
|
| 172 |
+
|
| 173 |
+
# Combine with wall constraints.
|
| 174 |
+
potential_r = np.minimum(wall_dists, potential_r_circles)
|
| 175 |
+
|
| 176 |
+
# New radii for this iteration (must be non-negative).
|
| 177 |
+
new_r = np.maximum(0.0, potential_r)
|
| 178 |
+
|
| 179 |
+
# Dampen the update to prevent oscillations.
|
| 180 |
+
radii = radii_old + current_uf * (new_r - radii_old)
|
| 181 |
+
|
| 182 |
+
# Early exit condition if radii have converged.
|
| 183 |
+
if np.max(np.abs(radii - radii_old)) < 1e-9:
|
| 184 |
+
break
|
| 185 |
+
|
| 186 |
+
return radii
|
| 187 |
+
# EVOLVE-BLOCK-END
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_105/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (5.41 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_105/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": false,
|
| 3 |
+
"error": "operands could not be broadcast together with shapes (3,) (26,) "
|
| 4 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_105/results/metrics.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"combined_score": 0.0,
|
| 3 |
+
"correct": false,
|
| 4 |
+
"primary": {
|
| 5 |
+
"combined_score": 0.0,
|
| 6 |
+
"execution_time_mean": 0.0,
|
| 7 |
+
"execution_time_std": 0.0,
|
| 8 |
+
"num_successful_runs": 0,
|
| 9 |
+
"num_valid_runs": 0,
|
| 10 |
+
"num_invalid_runs": 0,
|
| 11 |
+
"all_validation_errors": [],
|
| 12 |
+
"correct": false,
|
| 13 |
+
"validation_error": "operands could not be broadcast together with shapes (3,) (26,) "
|
| 14 |
+
},
|
| 15 |
+
"auxiliary": {},
|
| 16 |
+
"auxiliary_descriptions": {},
|
| 17 |
+
"timestamp": 1770405677.2668953,
|
| 18 |
+
"generation": 105
|
| 19 |
+
}
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_106/__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (7.16 kB). View file
|
|
|
examples_deprecated/circle_packing/results/results_full_gen200_period1000_20260206_165141/gen_106/results/correct.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"correct": true,
|
| 3 |
+
"error": null
|
| 4 |
+
}
|