Wednesday, April 16, 2025

Nonconvex problem: local vs multistart vs global

In [1] a somewhat abstract non-convex problem is given:

\[\begin{align}\min_x & - x_1^2 - x_2^2 - x_3^2 - x_4^2\\ & Ax \le b \end{align}\]

This is a nonconvex objective with some linear constraints. Of course, the easiest way to gain some insight into how to solve this is to perform some quick and dirty tests with a global QP solver designed for these types of models. As the constraints are linear, a solver like Cplex or Gurobi may be a good starting point (pun intended).

If you have access to a local NLP solver, it may not always be easy to find a good starting point. One possible approach is to use a multistart algorithm (some NLP solvers, like Baron and Knitro, have this built-in). This will not guarantee a global solution (and most likely, it doesn't give you one), but at least we can prevent really bad solutions.  

Model


It is very difficult to say much in the abstract. So let's create an example to put our teeth in. Here we have a transportation problem with a similar concave objective as in the original problem: \[\begin{align}\max&\sum_{(i,j)\in S}\color{darkred}x^2_{i,j} \\ & \sum_j \color{darkred}x_{i,j} \le \color{darkblue}{\mathit{supply}}_i && \forall i \\ & \sum_i \color{darkred}x_{i,j} \ge \color{darkblue}{\mathit{demand}}_j && \forall j \\ & \color{darkred}x_{i,j}\ge 0 \end{align}\] Here \(S\) is some subset of all \(\color{darkred}x_{i,j}\).  

For completeness: this model fits the constraints of the original post: \(Ax \le b\), as we can write our constraints as \[\begin{align}& \sum_j \color{darkred}x_{i,j} \le \color{darkblue}{\mathit{supply}}_i && \forall i \\ & \sum_i \left(-\color{darkred}x_{i,j}\right) \le -\color{darkblue}{\mathit{demand}}_j && \forall j \\ & -\color{darkred}x_{i,j}\le 0\end{align}\]

Data generation


Using random supply and demand data, I generate a model with 50 demand nodes and 50 supply nodes and 2500 arcs (i.e. 2500 variables). The non-linear objective is determined by the set \(S\), which is in this case the left-upper submatrix of \((i,j)\). I use the following three sizes: the first 3, 10, and 20 rows and columns. This leads to 9, 100, and 400 non-linear terms.

I used Conopt (an excellent large-scale local NLP solver) with a few trials with different starting points. I have no clue about constructing good initial points, so I just generate random starting points. (In the answers to [1] it is suggested that finding good initial points is very easy -- I totally fail to see how that would or even could work.) The global QP solver I used is Cplex. 


Size 1: 9 non-linear terms


This is a very easy problem. I used 10 multi-start iterations.

----    162 PARAMETER res  results (size=1)

                          status         obj        time

conopt    .trial1 .*    localopt    9572.357       0.032
conopt    .trial2 .     localopt
conopt    .trial3 .     localopt    4711.833       0.016
conopt    .trial4 .     localopt    8382.324       0.031
conopt    .trial5 .*    localopt    9600.243       0.016
conopt    .trial6 .     localopt    3029.130       0.015
conopt    .trial7 .     localopt                   0.016
conopt    .trial8 .     localopt    8035.934       0.031
conopt    .trial9 .     localopt                   0.016
conopt    .trial10.     localopt    9466.108       0.015
multistart.total  .                 9600.243       0.188
cplex     .       .    globalopt    9600.243       0.437

Note that the blank entries mean that the objective was zero. The records marked with an * are an improvement. We find the global optimal solution with conopt in trial 5. 

Cplex is doing an excellent job here:

        Nodes                                         Cuts/
   Node  Left     Objective  IInf  Best Integer    Best Bound    ItCnt     Gap

      0     0     9939.7624     0                   9939.7624        7         
*     0+    0                         9600.2432     9939.7624             3.54%
Found incumbent of value 9600.243195 after 0.02 sec. (23.55 ticks)
      0     2     9939.7624     0     9600.2432     9939.7624        7    3.54%
Elapsed time = 0.38 sec. (2003.49 ticks, tree = 0.02 MB)


Size 2: 100 non-linear terms


Here we used 25 trials:

 
----    162 PARAMETER res  results (size=2)

                          status         obj        time

conopt    .trial1 .*    localopt   19413.954       0.032
conopt    .trial2 .*    localopt   20443.936       0.032
conopt    .trial3 .     localopt   19391.875       0.031
conopt    .trial4 .     localopt   18962.226       0.016
conopt    .trial5 .     localopt   11144.281       0.016
conopt    .trial6 .     localopt   19914.037       0.031
conopt    .trial7 .     localopt   13040.606       0.015
conopt    .trial8 .     localopt   16662.541       0.031
conopt    .trial9 .     localopt    7131.035       0.016
conopt    .trial10.     localopt   18923.471       0.031
conopt    .trial11.     localopt   18073.915       0.032
conopt    .trial12.     localopt   11372.310       0.031
conopt    .trial13.     localopt   18614.723       0.031
conopt    .trial14.     localopt   17491.594       0.031
conopt    .trial15.     localopt    4861.867       0.015
conopt    .trial16.     localopt   19401.684       0.032
conopt    .trial17.     localopt   17091.372       0.031
conopt    .trial18.     localopt   14789.694       0.016
conopt    .trial19.     localopt   19026.705       0.032
conopt    .trial20.     localopt   16756.225       0.031
conopt    .trial21.     localopt   17776.559       0.016
conopt    .trial22.     localopt   13542.305       0.015
conopt    .trial23.*    localopt   20648.277       0.016
conopt    .trial24.     localopt   19895.247       0.031
conopt    .trial25.     localopt   16830.222       0.016
multistart.total  .                20648.277       0.627
cplex     .       .    globalopt   22491.252      61.641

The multistart algorithm finds a reasonable solution, but it is not optimal. There is about a 10% gap. The global solver needs more time to prove optimality.

Size 3: 400 non-linear terms


----    162 PARAMETER res  results (size=3)

                           status         obj        time

conopt    .trial1  .*    localopt   39316.482       0.047
conopt    .trial2  .*    localopt   44301.762       0.046
conopt    .trial6  .*    localopt   45559.234       0.047
conopt    .trial16 .*    localopt   45847.332       0.031
conopt    .trial23 .*    localopt   45922.772       0.031
conopt    .trial34 .*    localopt   46460.975       0.047
conopt    .trial133.*    localopt   46627.377       0.046
conopt    .trial179.*    localopt   47391.968       0.046
conopt    .trial210.*    localopt   47703.414       0.031
conopt    .trial297.*    localopt   48191.543       0.046
multistart.total   .                48191.543      43.254
cplex     .        .       nonopt   51650.218    1000.078

Here we used 1,000 multistart iterations. We only list the improvements here in the trace; otherwise, it would have 1,000 records. The global solver was stopped after hitting the time limit of 1,000 seconds. The gap at that time was 1.5%.

Multistart algorithm

The simplest multistart algorithm is also the one I used here:

  1. Create a random starting point
  2. Solve with a fast local solver
  3. If stopping criteria are met (e.g., time limit, iteration limit): STOP
  4. Go to step 1.  
This is usually very fast, but we can't say much about global optimality. There are some techniques to say something about the probability that a better solution exists (this can be used as a more meaningful stopping criterion).

GAMS is not very well suited for large loops around a solve statement. Every time the model will be generated. This is costly. The GAMS scenario solver removes this burden by executing the loop inside the solver instead of GAMS itself. Unfortunately, the GAMS scenario solver does not support a multistart algorithms. The developers just forgot about this. 

Global solvers


Deterministic global solvers behave much like MIP solvers. In fact, they use a branch-and-bound strategy, just like a MIP solver. We also can see this in how the "bounds" (best found solution so far vs. best possible bound) converge over time:

The blue line here represents the solutions found. It is increasing as we are maximizing. The red line is what the solver knows about the best possible solution. We see that:
  1. Most of the excitement happens at the beginning: the solver finds quickly very good solutions, and the best bound is decreasing rapidly.
  2. The tail end is always a long slug: the solver is trying to prove optimality, mainly by working on the best bound.
  3. Often (but not always), the global optimal solution is found relatively quicky. But then, we need a lot of time proving optimality.
  4. In practice, we may set a time limit or use a stopping rule on the gap.
  5. We could have passed on the best multistart solution. This is automatic for global solvers like Baron. However, I don't know how to do that with Cplex. This is not a great loss, as Cplex often finds very good solutions very quickly. With a better starting point, we basically would have shaved some time from the early part of the solution process, where Cplex is already quite effective.



Data set
----     32 PARAMETER supply  

supply1  17.175,    supply2  84.327,    supply3  55.038,    supply4  30.114,    supply5  29.221,    supply6  22.405
supply7  34.983,    supply8  85.627,    supply9   6.711,    supply10 50.021,    supply11 99.812,    supply12 57.873
supply13 99.113,    supply14 76.225,    supply15 13.069,    supply16 63.972,    supply17 15.952,    supply18 25.008
supply19 66.893,    supply20 43.536,    supply21 35.970,    supply22 35.144,    supply23 13.149,    supply24 15.010
supply25 58.911,    supply26 83.089,    supply27 23.082,    supply28 66.573,    supply29 77.586,    supply30 30.366
supply31 11.049,    supply32 50.238,    supply33 16.017,    supply34 87.246,    supply35 26.511,    supply36 28.581
supply37 59.396,    supply38 72.272,    supply39 62.825,    supply40 46.380,    supply41 41.331,    supply42 11.770
supply43 31.421,    supply44  4.655,    supply45 33.855,    supply46 18.210,    supply47 64.573,    supply48 56.075
supply49 76.996,    supply50 29.781


----     32 PARAMETER demand  

demand1   69.574,    demand2   79.046,    demand3   66.208,    demand4   31.850,    demand5   12.106,    demand6   13.715
demand7   67.589,    demand8   57.995,    demand9    6.616,    demand10  82.700,    demand11  10.740,    demand12  21.030
demand13  56.027,    demand14  78.485,    demand15  21.276,    demand16   6.878,    demand17  61.977,    demand18  65.587
demand19  42.400,    demand20  39.335,    demand21  27.767,    demand22  28.106,    demand23  16.514,    demand24  96.809
demand25  41.458,    demand26  81.804,    demand27  33.467,    demand28  16.012,    demand29  78.351,    demand30  10.387
demand31  23.665,    demand32   3.970,    demand33  30.425,    demand34  53.449,    demand35  18.592,    demand36  20.881
demand37  36.528,    demand38  35.154,    demand39  35.672,    demand40  99.861,    demand41 102.824,    demand42  40.454
demand43  40.753,    demand44  80.662,    demand45  43.132,    demand46  94.773,    demand47  15.422,    demand48  77.012
demand49   9.006,    demand50  61.094


----     32 PARAMETER totsup               =     2245.137  total supply
            PARAMETER totdem               =     2245.137  total demand

----     32 SET subij  subset to construct objective

            demand1     demand2     demand3

supply1         YES         YES         YES
supply2         YES         YES         YES
supply3         YES         YES         YES

Solution
----     90 VARIABLE z.L                   =     9600.243  objective

----     90 VARIABLE x.L  flow from supply to demand nodes

             demand1     demand2     demand3     demand4     demand5     demand6     demand7     demand8     demand9    demand10

supply1                               17.175
supply2        5.281      79.046
supply3       55.038
supply5                                                       12.106
supply8                                                                   13.715
supply14                                                                                                                  76.225
supply15                                                                                                       6.616
supply16                                                                              11.282
supply18                                                                                           2.664
supply20                                          25.248
supply22                                                                                          35.144
supply23                                                                              13.149
supply33       0.579
supply34       8.677                  43.415
supply37                                                                              43.157
supply38                               5.619       2.251                                                                   6.475
supply43                                           4.351
supply49                                                                                          18.185
supply50                                                                                           2.002

       +    demand11    demand12    demand13    demand14    demand15    demand16    demand17    demand18    demand19    demand20

supply4                                                                                           16.730
supply8                                           37.154                              11.917
supply12                  19.299
supply19                              24.911
supply24                               6.005
supply28                                                                              50.059
supply30                                                      21.276                                           9.090
supply32                                                                                          48.857
supply36                              25.111
supply38                                                                                                                  39.335
supply39                   1.731
supply41                                          41.331
supply47                                                                                                      33.310
supply50      10.740                                                       6.878

       +    demand21    demand22    demand23    demand24    demand25    demand26    demand27    demand28    demand29    demand30

supply6                                                                               22.405
supply8                                                                                                       22.841
supply12                                          37.997
supply13                                                                  75.351
supply15                                                                   6.453
supply16      27.767
supply17                  15.952
supply21                                                                                                      35.970
supply25                                                                                                      18.159
supply26                  12.154
supply28                              16.514
supply29                                                                                          16.012
supply32                                                                                                       1.382
supply37                                                                              11.062                               5.176
supply48                                                      41.458
supply49                                          58.812
supply50                                                                                                                   5.210

       +    demand31    demand32    demand33    demand34    demand35    demand36    demand37    demand38    demand39    demand40

supply5                                                                                                                    1.693
supply10      23.665
supply11                                          35.161                  11.028
supply13                                                                                                                  23.763
supply20                                          18.287
supply26                                                                                                                  70.935
supply34                                                                                          35.154
supply36                                                                                                                   3.470
supply38                                                      18.592
supply40                                                                   9.852      36.528
supply45                   3.430      30.425
supply47                                                                                                      31.263
supply50                   0.540                                                                               4.410

       +    demand41    demand42    demand43    demand44    demand45    demand46    demand47    demand48    demand49    demand50

supply4                   13.384
supply5                                                                               15.422
supply7       34.983
supply9        6.711
supply10                                          26.356
supply11                                          53.622
supply12                                                                   0.578
supply16                                                      24.922
supply18      22.344
supply19                                                                  41.982
supply24                                                                                                       9.006
supply25                              40.753
supply27      23.082
supply29                                                                                          61.574
supply31      11.049
supply33                                                                                          15.438
supply35                                           0.684                  25.828
supply39                                                                                                                  61.094
supply42                                                                  11.770
supply43                  27.070
supply44       4.655
supply46                                                      18.210
supply48                                                                  14.617

GAMS model
$onText

Nonconvex problem with linear inequality constraints

$offText

* three data sets
$set size 1
*$set size 2
*$set size 3

table sizedata(*,*) 'sizing'
NLsize QuadTerms MultistartIts CompactResults
size1 3 9 10 0
size2 10 100 25 0
size3 20 400 1000 1
* NLsize: size of left-upper (i,j) subset
* QuadTerms: NLsize^2
* MultistartIts: iterations for multistart algorithm
* CompactResults: 1=only list improvements


*---------------------------------------------------------------
* generate data for balanced transportation problem
*---------------------------------------------------------------

set
i 'supply nodes' /supply1*supply50/
j 'demand nodes' /demand1*demand50/
;

parameters
supply(i)
demand(j)
totsup 'total supply'
totdem 'total demand'
;

supply(i) = uniform(0,100);
demand(j) = uniform(0,100);

* make sure transportation problem is balanced
totsup = sum(i,supply(i));
totdem = sum(j,demand(j));

if (totsup>totdem,
demand(j) = demand(j) + (totsup-totdem)/card(j);
else
supply(i) = supply(i) + (totdem-totsup)/card(i);
);

totsup = sum(i,supply(i));
totdem = sum(j,demand(j));

display supply,demand,totsup,totdem;

*---------------------------------------------------------------
* a small subset has nonlinear terms in the objective
*---------------------------------------------------------------


scalar
NL 'square root of NL terms in obj'
;

NL = sizeData('size%size%','NLsize');

set subij(i,j) 'sub matrix';
subij(i,j) = ord(i)<=NL and ord(j)<=NL;

scalar nn 'number of non-linear terms';
nn = card(subij);
display nn;

*---------------------------------------------------------------
* concave nlp model
*---------------------------------------------------------------


Variables
z 'objective'
;
positive variables
x(i,j) 'flow from supply to demand nodes'
;

equations
obj
esupply
edemand
;

obj.. z =e= sum(subij,sqr(x(subij)));
esupply(i).. sum(j, x(i,j)) =l= supply(i);
edemand(j).. -sum(i, x(i,j)) =l= -demand(j);

model m /all/;

*---------------------------------------------------------------
* simple multistart algorithm
*---------------------------------------------------------------

parameter startx(i,j);

option qcp = conopt;

Sets
trialss 'superset' /trial1*trial10000/
trial(trialss) 'actual number of trials'
;
trial(trialss) = ord(trialss) <= sizeData('size%size%','MultistartIts');

* faster iterations
m.solprint = %solPrint.silent%;
m.solvelink = %solveLink.loadLibrary%;

parameter
res(*,*,*,*) 'results (size=%size%)'
best /-INF/
time /0/
improv 'improvement'
opt 'optimal'
print 'print this record'
printflag '1=all,0=only improvements'
;
option res:3:3:1;
singleton set imprv(*) 'improvement in trace';
printflag = 1-sizeData('size%size%','CompactResults');
acronym localopt,nonopt,globalopt;
loop(trial,
x.l(i,j) = uniform(1,100);
solve m maximizing z using qcp;
time = time + m.resusd;
opt = m.solvestat=%modelStat.Optimal% or m.solvestat=%modelStat.locallyOptimal%;
improv = opt and z.l>best;
print = printflag or improv;
best$improv = z.l;
if (print,
if (improv,
imprv('*') = yes;
else
imprv(' ') = yes;
);
res('conopt',trial,imprv,'status') = nonopt;
res('conopt',trial,imprv,'status')$opt = localopt;
res('conopt',trial,imprv,'obj') = z.l;
res('conopt',trial,imprv,'time') = m.resusd;
);
);
res('multistart','total',' ','obj') = best;
res('multistart','total',' ','time') = time;

*---------------------------------------------------------------
* global qp solver
*---------------------------------------------------------------

option qcp=cplex, reslim=1000;

m.optfile=1;
solve m maximizing z using qcp;
res('cplex',' ',' ','status') = nonopt;
res('cplex',' ',' ','status')$(m.solvestat=%modelStat.Optimal%)=globalopt;
res('cplex',' ',' ','obj') = z.l;
res('cplex',' ',' ','time') = m.resusd;
display res;

display z.l,x.l;

$onecho > cplex.opt
OptimalityTarget 3
$offecho


Conclusions


My conclusions:
  • I don't know how to generate a good starting point.
  • A single run with a local NLP solver is not very useful: there is not much we can say about a solution found this way. It could be very bad or very good.
  • The multistart approach is easy to implement, and finds reasonable solutions quickly. In general, it will not find global solutions.
  • A global QP solver delivers solutions that are proven globally optimal, but it is expensive for larger problems. Often, we can stop a bit early, as much of the time is spent on proving optimality.

References

  1. Efficient Approaches for Separable Concave Quadratic Minimization over Linear Constraints?, https://or.stackexchange.com/questions/13081/efficient-approaches-for-separable-concave-quadratic-minimization-over-linear-co

No comments:

Post a Comment