1+ # EVOLVE-BLOCK-START
2+ """Advanced circle packing for n=26 circles in a unit square"""
3+ import numpy as np
4+ from scipy .optimize import minimize
5+
6+ def construct_packing ():
7+ """
8+ Construct an optimized arrangement of 26 circles in a unit square
9+ using mathematical principles and optimization techniques.
10+
11+ Returns:
12+ Tuple of (centers, radii, sum_of_radii)
13+ centers: np.array of shape (26, 2) with (x, y) coordinates
14+ radii: np.array of shape (26) with radius of each circle
15+ sum_of_radii: Sum of all radii
16+ """
17+ n = 26
18+
19+ # Initial guess: Strategic placement with some randomness
20+ centers = np .zeros ((n , 2 ))
21+ radii = np .zeros (n )
22+
23+ # Heuristic placement for better initial guess: place larger circles in center
24+ radii [:] = np .linspace (0.12 , 0.05 , n ) # Linear distribution of radii
25+
26+ # Initial placement: approximate hexagonal grid
27+ grid_x = int (np .sqrt (n ))
28+ grid_y = int (n / grid_x )
29+
30+ x_coords = np .linspace (0.15 , 0.85 , grid_x )
31+ y_coords = np .linspace (0.15 , 0.85 , grid_y )
32+
33+ count = 0
34+ for i in range (grid_x ):
35+ for j in range (grid_y ):
36+ if count < n :
37+ centers [count ] = [x_coords [i ] + 0.05 * (j % 2 ), y_coords [j ]]
38+ count += 1
39+
40+ # Place remaining circles randomly
41+ while count < n :
42+ centers [count ] = np .random .rand (2 ) * 0.7 + 0.15
43+ count += 1
44+
45+ # Objective function: Negative sum of radii (to maximize)
46+ def objective (x ):
47+ centers = x [:2 * n ].reshape (n , 2 )
48+ radii = x [2 * n :]
49+ return - np .sum (radii )
50+
51+ # Constraint: No overlaps and circles stay within the unit square
52+ def constraint (x ):
53+ centers = x [:2 * n ].reshape (n , 2 )
54+ radii = x [2 * n :]
55+
56+ # Overlap constraint
57+ overlap_constraints = []
58+ for i in range (n ):
59+ for j in range (i + 1 , n ):
60+ dist = np .sqrt (np .sum ((centers [i ] - centers [j ])** 2 ))
61+ overlap_constraints .append (dist - (radii [i ] + radii [j ]))
62+
63+ # Boundary constraints
64+ boundary_constraints = []
65+ for i in range (n ):
66+ boundary_constraints .append (centers [i , 0 ] - radii [i ]) # x >= radius
67+ boundary_constraints .append (1 - centers [i , 0 ] - radii [i ]) # x <= 1 - radius
68+ boundary_constraints .append (centers [i , 1 ] - radii [i ]) # y >= radius
69+ boundary_constraints .append (1 - centers [i , 1 ] - radii [i ]) # y <= 1 - radius
70+
71+ return np .array (overlap_constraints + boundary_constraints )
72+
73+ # Initial guess vector
74+ x0 = np .concatenate ([centers .flatten (), radii ])
75+
76+ # Bounds: Circles stay within the unit square and radii are positive
77+ bounds = [(0 , 1 )] * (2 * n ) + [(0.03 , 0.2 )] * n # radii are positive, up to 0.2
78+
79+ # Constraints dictionary
80+ constraints = {'type' : 'ineq' , 'fun' : constraint }
81+
82+ # Optimization using SLSQP
83+ result = minimize (objective , x0 , method = 'SLSQP' , bounds = bounds , constraints = constraints , options = {'maxiter' : 1000 , 'ftol' : 1e-8 })
84+
85+ # Extract optimized centers and radii
86+ optimized_centers = result .x [:2 * n ].reshape (n , 2 )
87+ optimized_radii = result .x [2 * n :]
88+
89+ # Ensure radii are not negative (numerical stability)
90+ optimized_radii = np .maximum (optimized_radii , 0.001 )
91+
92+ # Calculate the sum of radii
93+ sum_radii = np .sum (optimized_radii )
94+
95+ return optimized_centers , optimized_radii , sum_radii
96+ # EVOLVE-BLOCK-END
97+
98+ # This part remains fixed (not evolved)
99+ def run_packing ():
100+ """Run the circle packing constructor for n=26"""
101+ centers , radii , sum_radii = construct_packing ()
102+ return centers , radii , sum_radii
103+
104+ def visualize (centers , radii ):
105+ """
106+ Visualize the circle packing
107+
108+ Args:
109+ centers: np.array of shape (n, 2) with (x, y) coordinates
110+ radii: np.array of shape (n) with radius of each circle
111+ """
112+ import matplotlib .pyplot as plt
113+ from matplotlib .patches import Circle
114+
115+ fig , ax = plt .subplots (figsize = (8 , 8 ))
116+
117+ # Draw unit square
118+ ax .set_xlim (0 , 1 )
119+ ax .set_ylim (0 , 1 )
120+ ax .set_aspect ('equal' )
121+ ax .grid (True )
122+
123+ # Draw circles
124+ for i , (center , radius ) in enumerate (zip (centers , radii )):
125+ circle = Circle (center , radius , alpha = 0.5 )
126+ ax .add_patch (circle )
127+ ax .text (center [0 ], center [1 ], str (i ), ha = 'center' , va = 'center' )
128+
129+ plt .title (f"Circle Packing (n={ len (centers )} , sum={ sum (radii ):.6f} )" )
130+ plt .show ()
131+
132+ if __name__ == "__main__" :
133+ centers , radii , sum_radii = run_packing ()
134+ print (f"Sum of radii: { sum_radii } " )
135+ # AlphaEvolve improved this to 2.635
136+
137+ # Uncomment to visualize:
138+ # visualize(centers, radii)
0 commit comments