The goal of this exercise is to fill a square area \([0,250]\times[0,100]\) with 25 circles. The model can choose the \(x\) and \(y\) coordinates of the center of each circle and the radius. So we have as variables \(\color{darkred}x_i\), \(\color{darkred}y_i\), and \(\color{darkred}r_i\). The circles placed inside the area should not overlap. The objective is to maximize the total area covered.
A solution is:
The optimization model can look like:
Non-convex Quadratic Model |
---|
\[\begin{align} \max&\sum_i \color{darkblue}\pi\cdot\color{darkred}r_i^2 && &&\text{maximize covered area}\\ &(\color{darkred}x_i-\color{darkred}x_j)^2 + (\color{darkred}y_i-\color{darkred}y_j)^2 \ge (\color{darkred}r_i+\color{darkred}r_j)^2 && \forall i\lt j && \text{no overlap} \\ & \color{darkred}x_i-\color{darkred}r_i \ge 0 && && \text{stay inside area} \\ & \color{darkred}x_i+\color{darkred}r_i \le \color{darkblue}{\mathit{sizeX}} \\ & \color{darkred}y_i-\color{darkred}r_i \ge 0 \\ & \color{darkred}y_i+\color{darkred}r_i \le \color{darkblue}{\mathit{sizeY}} \\ & \color{darkred}r_i \le \color{darkred}r_{i-1} && \forall i\gt 1&& \text{optional symmetry breaker}\\ & \color{darkred}x_i \in [0,\color{darkblue}{\mathit{sizeX}}] && && \text{bounds} \\ &\color{darkred}y_i \in [0,\color{darkblue}{\mathit{sizeY}}] \\ & \color{darkred}r_i \in \left[0,\frac{\min(\color{darkblue}{\mathit{sizeX}},\color{darkblue}{\mathit{sizeY}})}{2}\right]\end{align}\] |
The objective is simply maximizing the area covered by all circles. Actually, I reformulated this into an objective that measures the percentage of the covered area. The pairwise no-overlap constraints are defined over only \(i \lt j\) to prevent checking a pair of circles twice. The symmetry-breaking constraint reduces the size of the problem. Note that we have both a non-convex quadratic objective and a non-convex no-overlap constraint.
The model is small: 75 variables and 424 constraints. However, it is somewhat of a nightmare for global solvers. It is extremely difficult to prove optimality. We can find an excellent starting solution by using a simple multi-start algorithm with a local NLP solver. In fact, in this experiment, the multi-start algorithm found the best solution. The global solvers did not improve on this.
The results are:
---- 156 VARIABLE z.L = 91.183covered area (% of area) ---- 162 PARAMETER results x y r circle1 200.00050.00050.000 circle2 50.00050.00050.000 circle3 125.00028.12528.125 circle4 116.66777.77822.222 circle5 150.00087.50012.500 circle6 92.8579.1849.184 circle7 157.1439.1849.184 circle8 142.30860.5778.654 circle9 8.57991.4218.579 circle10 241.4218.5798.579 circle11 241.42191.4218.579 circle12 8.5798.5798.579 circle13 90.00092.0008.000 circle14 166.66794.4445.556 circle15 104.54553.7664.675 circle16 80.0004.5004.500 circle17 170.0004.5004.500 circle18 4.28920.7114.289 circle19 245.71120.7114.289 circle20 4.28979.2894.289 circle21 150.00070.8334.167 circle22 78.57195.9184.082 circle23 142.30873.0773.846 circle24 155.40521.2843.041 circle25 2.56672.6542.566
Conclusions
- These models are very difficult for global solvers.
- This is a nice benchmark problem as we can scale it from very small to very large. In addition, humans can easily assess the quality of the solution by visual inspection of a plot. We can easily spot bad solutions. Even very small instances can be very tough to solve to proven optimality.
- Multi-start NLP is quite effective in finding good solutions.
- GAMS support for multi-start NLP algorithms is limited and can be improved. We want to generate the model just once and use parallel execution. This use case would be a good candidate for the scenario solver Guss. It is inexplicable why this is not supported.
- The optional symmetry-breaking constraint is beneficial when using global solvers. Global solvers behave more like MIP solvers, where such constraints can be highly effective. A tiny 4 circle problem yielded the following timings:
Without symmetry constraint:
313360 Nodes explored
1968.00 Total time (wall s)
With symmetry constraint:
25759 Nodes explored
86.00 Total time (wall s) - However, the symmetry constraint hurts when using the multi-start algorithm. Local solvers take more time to become feasible and, in general, find worse local optima. For the local search, it is better to turn off the symmetry constraint. To illustrate, here are the results of 100 trials with 10 circles:
With symmetry constraint:
---- 112 PARAMETER zbest = 83.686 best objective
Without symmetry constraint:
---- 112 PARAMETER zbest = 86.096 best objective - A solver like Baron has a built-in facility to perform a multi-start local search before it really starts working on the global problem. Here, I argue that it may be better to separate these phases.
Appendix: GAMS model
$onText
Place 25 circles (size is endogenous) on a [0,250]x[0,100] square area such that circles don't overlap and the total area covered is maximized.
Very tough little non-convex NLP.
Obj MultiStart 91.183 (1015 trials, w/o symmetry constraint) Baron 91.183 (no improvement, 1 hour, with symmetry breaker)
$offText
*------------------------------------------------------------------- * size of problem *-------------------------------------------------------------------
Set i 'circles' /circle1*circle25/; alias (i,j);
set ij(i,j) 'compare circles i and j: only i<j'; ij(i,j) = ord(i) < ord(j);
scalars sizeX 'size of area' /250/ sizeY 'size of area' /100/ ;
*------------------------------------------------------------------- * NLP Model *-------------------------------------------------------------------
positive variables x(i) 'x-coordinate' y(i) 'y-coordinate' r(i) 'radius' ;
x.up(i) = sizeX; y.up(i) = sizeY; r.up(i) = min(sizeX,sizeY)/2;
variable z 'covered area (% of area)';
equations cover 'calculate total area covered' no_overlap(i,j) 'circles cannot overlap' xlo(i) 'stay inside [0,250]' xup(i) 'stay inside [0,250]' ylo(i) 'stay inside [0,100]' yup(i) 'stay inside [0,100]' symmetry(i) 'symmetry breaker' ;
cover.. z =e= 100*sum(i, pi*sqr(r(i))) / (sizeX*sizeY); no_overlap(ij(i,j)).. sqr(x(i)-x(j)) + sqr(y(i)-y(j)) =g= sqr(r(i)+r(j)); xlo(i).. x(i) - r(i) =g= 0; xup(i).. x(i) + r(i) =l= sizeX; ylo(i).. y(i) - r(i) =g= 0; yup(i).. y(i) + r(i) =l= sizeY; symmetry(i-1) .. r(i) =l= r(i-1);
* improves logging of gap, but may slow things down * (nl obj becomes a constraint) z.up = 100;
model circlesw 'with symmetry' /all/ ; model circleswo 'w/o symmetry' /circlesw - symmetry/ ;
*------------------------------------------------------------------- * select algorithm *-------------------------------------------------------------------
$set MultiStart 1 $set Global 1
*------------------------------------------------------------------- * multistart approach using local NLP solver * best used without symmetry breaker * we get a solution: 91.183 @ trial1015 *-------------------------------------------------------------------
$ifThen %MultiStart% == 1
set k /trial1*trial1015/; option qcp = conopt; parameter best(i,*) 'best solution'; scalar zbest 'best objective' /0/; parameter trace(k) 'keep track of improvements'; circleswo.solprint = %solprint.Silent%; circleswo.solvelink = 5; loop(k, x.l(i) = uniform(10,sizeX-10); y.l(i) = uniform(10,sizeY-10); r.l(i) = uniform(10,40); solve circleswo maximizing z using qcp; if(z.l>zbest, zbest = z.l; best(i,'x') = x.l(i); best(i,'y') = y.l(i); best(i,'r') = r.l(i); trace(k) = z.l; ); );
display zbest,best,trace;
* ugly code to sort on r so we can use symmetry constraint in * subsequent solve. set rem(i) 'remaining'; rem(i) = yes; option strictSingleton = 0; singleton set cur(i) 'current'; scalar largest; loop(i, largest = smax(rem,best(rem,'r')); cur(rem) = best(rem,'r')=largest; x.l(i) = best(cur,'x'); y.l(i) = best(cur,'y'); r.l(i) = best(cur,'r'); rem(cur) = no; ); z.l = zbest;
* reset to default for next solves circleswo.solprint = %solprint.On%;
$endIf
*------------------------------------------------------------------- * Global NLP solver * with or without symmetry breaker *-------------------------------------------------------------------
$ifThen %Global% == 1
option qcp=baron, reslim=3600; option threads=-1; solve circlesw maximizing z using qcp;
$endIf
*------------------------------------------------------------------- * Reporting *-------------------------------------------------------------------
display z.l;
parameter results(i,*); results(i,'x') = x.l(i); results(i,'y') = y.l(i); results(i,'r') = r.l(i); display results; |
- When we find a solution with the multi-start local solver algorithm, we have the \(\color{darkred}r_i\) unsorted. To be able to use this solution as a starting point for the global solver we (assuming it uses the symmetry breaker constraint), we need to sort the solution so it is feasible w.r.t. to the symmetry constraint.