MeshKit  1.0
IARoundingFarNlp.cpp
Go to the documentation of this file.
```00001 // IARoundingFarNlp.cpp
00002 // Interval Assignment for Meshkit
00003 //
00007 // This code is published under the Eclipse Public License.
00008 //
00009 // \$Id: hs071_nlp.cpp 1864 2010-12-22 19:21:02Z andreasw \$
00010 //
00011 // Authors:  Carl Laird, Andreas Waechter     IBM    2005-08-16
00012
00013 #include "IARoundingFarNlp.hpp"
00014 #include "IARoundingNlp.hpp"
00016 #include "IPData.hpp"
00017 #include "IASolution.hpp"
00018 #include "IANlp.hpp"
00019
00020 #include <math.h>
00021 #include <algorithm>
00022
00023 // for printf
00024 #ifdef HAVE_CSTDIO
00025 # include <cstdio>
00026 #else
00027 # ifdef HAVE_STDIO_H
00028 #  include <stdio.h>
00029 # else
00030 #  error "don't have header file for stdio"
00031 # endif
00032 #endif
00033
00034 /* Idea: a form of IARoundingNlp with larger variable bounds, but still with a natural integer solution.
00035  x in [1..inf]
00036  xr = x optimal relaxed solution with objective function fnlp, see IANlp.xpp
00037  f is piecewise linear, with corners at integer values. f slopes are unique (we hope)
00038  Slope definitions
00039  for x between xl = floor xr and xh = ceil xr, we use the difference in fnlp between xl and xh
00040
00041  case A. xr > ceil g, g is goal I[i]
00042  for x above xh,
00043  let h+ be fnlp ( xh+1 ) - fnlp ( xh )
00044  let kp be the number of intervals x is above xh
00045  then slope = floor kp * h+
00046  for x below xl, h- = sqrt(11) / 5 h+, and slope = floor km * h-
00047  all this is weighted by some unique weight
00048
00049  case B. xr in [ floor g, ceil g]
00050  h+ = fnlp ( xh+1 ) - fnlp ( xh )
00051  h- = fnlp ( xl-1 ) - fnlp ( xl ), not used if xl == 1
00052
00053  case C. xr < floor g
00054  h- = fnlp ( xl-1 ) - fnlp ( xl )
00055  h+ = sqrt(10)/5 h-
00056  If g < 2, then h- is unused, and h+ = as in case B
00057
00058  // representation:
00059  h0 is weights 0..n-1
00060  h+ is weights n..2n-1
00061  h- is weights 2n..
00062
00063 */
00064
00065
00066 // to do
00067 // set h
00068 // uniquify h initially
00069 // randomize h if solution is non-integer
00070 // compute f
00071 // compute f'
00072 // compute f'' == 0
00073
00074 // clean up
00075 // move the re-used part of IANlp, dealing with constraints but not obj, to some underlying class, without it being a TNLP
00076
00077
00078 // the objective function we should use for deriving the weights
00079 double IARoundingFarNlp::f_x_value( double I_i, double x_i ) const
00080 {
00081   // to do revise
00082   return x_i > I_i ?
00083   (x_i - I_i) / I_i :
00084   1.103402234045 * (I_i - x_i) / x_i;
00085 }
00086
00087 // destructor
00088 IARoundingFarNlp::~IARoundingFarNlp()
00089 {
00090   data = NULL;
00091   ipData = NULL;
00092   solution = NULL;
00093 }
00094
00095 // constructor
00096 IARoundingFarNlp::IARoundingFarNlp(const IAData *data_ptr, const IPData *ip_data_ptr, IASolution *solution_ptr):
00097 data(data_ptr), ipData(ip_data_ptr), solution(solution_ptr), baseNlp(data_ptr, solution_ptr),
00098 debugging(true), verbose(true) // true
00099 {
00100   printf("\nIARoundingFarNlp Problem:\n");
00101
00102   // weights for function value
00103   h0.resize(data->num_variables());
00104   hp.resize(data->num_variables());
00105   hm.resize(data->num_variables());
00106   for (int i = 0; i < data->num_variables(); ++i)
00107   {
00108     const double x = ipData->relaxedSolution[i];
00109     const double xl = floor(x);
00110     const double xh = xl + 1;
00111     const double g = data->I[i]; // goal
00112
00113     const double fl = f_x_value(g, xl);
00114     const double fh = f_x_value(g, xh);
00115     const double w = fh - fl;
00116     h0[i] = w;
00117
00118     // use sqrt(3) to ensure hp and hm is larger than h0
00119     const double fp = sqrt(3) * (f_x_value(g,xh+1) - f_x_value(g,xh));
00120     double fm = 0.;
00121
00122     if (xl > 1)
00123     {
00124       fm = sqrt(5) * (f_x_value(g,xl) - f_x_value(g,xl-1));
00125     }
00126     else // if (xl == 1)
00127     {
00128       // unused anyway, assign it something that won't screw up the distribution
00129       fm = - sqrt(5) * fabs(f_x_value(g,2) - f_x_value(g,1));
00130     }
00131
00132     // case A
00133     if ( x > ceil(g) )
00134     {
00135       hp[i] = fp;
00136       hm[i] = -fp * sqrt(11) / 5.;
00137       assert( fabs(hm[i]) < fabs(hp[i]) );
00138     }
00139     // case B
00140     else if ( x >= floor(g) )
00141     {
00142       hp[i] = fp;
00143       hm[i] = fm;
00144     }
00145     // case C
00146     else // x < floor(g)
00147     {
00148       hm[i] = fm;
00149       hp[i] = -fm * sqrt(7) / 3.;
00150       assert( fabs(hm[i]) > fabs(hp[i]) );
00151     }
00152
00153     // invariants for all cases, to produce a super-linear preference for xl or xh
00154     assert( hp[i] > 0 );
00155     assert( hp[i] > h0[i] );
00156     assert( hm[i] < 0 );
00157     assert( hm[i] < h0[i] );
00158
00159     /*
00160     //zzyk experiment with limited f' discontinuity
00161      // this makes hp < 0 and hm > 0 sometimes, so watch the other asserts
00162     if ( h0[i] > 0 )
00163     {
00164       hp[i] = h0[i]*2.;
00165       hm[i] = h0[i]*0.5;
00166     }
00167     else
00168     {
00169       hp[i] = h0[i]*0.5;
00170       hm[i] = h0[i]*2.;
00171     }
00172      */
00173
00174     if (0 && debugging)
00175       printf(": %f %f %f", h0[i], hp[i], hm[i]); // 0th+/i, first should be positive, second negative
00176   }
00177
00178   // uniquify and scale the weights = slopes
00179   const double h0_lo = 1;
00180   const double h0_hi = h0_lo * (10 + 2 * data->num_variables());
00181   // uniquify preserving h0 < hm, hp
00182   if (0)
00183   {
00184     // todo experiment with ranges
00185     // want the largest h0 to be less then the smallest hp or hm, in absolute value
00186     // so that a simple rounding from the relaxed solution is optimal, if feasible
00187     // 1.e4? 1.e3 makes the jumps in values wash out for the big chain problem, need some jumps?
00188     // idea, dynamically scale this based on the problem size, h0_hi = 2*num_variables;
00189     const double hpm_lo = h0_hi + 1.;
00190     const double hpm_hi = 1.e5;
00191     h0.uniquify(h0_lo, h0_hi);
00192
00193     // uniquify and scale hp hm to come after h0
00194     IAWeights hpm();
00195     hpm = hp;                 // hpm = hp
00196     hpm.insert( hpm.end(), hm.begin(), hm.end() ); // hmp += hm
00197     hpm.uniquify(hpm_lo, hpm_hi);
00198     hp.assign(hpm.begin(), hpm.begin()+hp.size()); // extract hp
00199     hm.assign(hpm.begin()+hp.size(), hpm.end());   // extract hm
00200   }
00201   // uniquify lumping h0 with hm and hp
00202   else {
00203     h0.uniquify(h0, h0_lo, h0_hi);
00204
00205     // uniquify and scale hp hm to come after h0
00206     IAWeights h0pm;
00207     hpm = h0;                  // h0pm = h0
00208     h0pm.insert( h0pm.end(), hp.begin(), hp.end() ); // h0pm += hp
00209     h0pm.insert( h0pm.end(), hm.begin(), hm.end() ); // h0pm += hm
00210     h0pm.uniquify(h0_lo, h0_hi);
00211     const size_t sz = data->num_variables();
00212     h0.assign(h0pm.begin(),      h0pm.begin()+sz);   // extract h0
00213     hp.assign(h0pm.begin()+sz,   h0pm.begin()+2*sz); // extract hp
00214     hm.assign(h0pm.begin()+2*sz, h0pm.end());        // extract hm
00215
00216   }
00217
00218   if (debugging)
00219   {
00220     printf("\n");
00221     for (int i = 0; i < data->num_variables(); ++i)
00222     {
00223       const double x = ipData->relaxedSolution[i];
00224       printf("x %d (relaxed %f, goal %e), h0 %10.4f hp %10.4f hm %10.4f\n", i, x, data->I[i], h0[i], hp[i], hm[i]);
00225     }
00226   }
00227 }
00228
00229
00230 // returns the size of the problem
00231 bool IARoundingFarNlp::get_nlp_info(Index& n, Index& m, Index& nnz_jac_g,
00232                              Index& nnz_h_lag, IndexStyleEnum& index_style)
00233 {
00234   bool base_ok = baseNlp.get_nlp_info(n, m, nnz_jac_g, nnz_h_lag, index_style);
00235   nnz_h_lag = 0; // obj is piecewise linear. We could define h near the corners, but lets try zero first. nele_hess
00236   return true && base_ok;
00237   // need to change this if there are more variables, such as delta-minuses
00238 }
00239
00240 // returns the variable bounds
00241 bool IARoundingFarNlp::get_bounds_info(Index n, Number* x_l, Number* x_u,
00242                                 Index m, Number* g_l, Number* g_u)
00243 {
00244   const bool base_ok = baseNlp.get_bounds_info(n, x_l, x_u, m, g_l, g_u);
00245
00246   // test various ranges
00247   /*
00248   for (Index i=0; i<n; ++i)
00249   {
00250   */
00251
00252     /*
00253     // debug: x in [xl-1, xh+1]
00254     const double x = ipData->relaxedSolution[i];
00255     const double xl = floor(x);
00256     assert(xl >= 1.);
00257     if (xl > 1)
00258       x_l[i] = xl-1;
00259     else
00260       x_l[i] = 1;
00261     x_u[i] = xl + 2;
00262     */
00263
00264     /*
00265     // debug: x in [xl,xh]
00266     x_l[i] = xl;
00267     x_u[i] = xl + 1.;
00268     */
00269
00270     //if (debugging)
00271     //    printf("x %d (%f) in [%d,%d]\n", i, x, (int) x_l[i], (int) x_u[i]);
00272
00273   /*
00274   }
00275   */
00276
00277   return base_ok && true;
00278 }
00279
00280 // returns the initial point for the problem
00281 bool IARoundingFarNlp::get_starting_point(Index n, bool init_x, Number* x_init,
00282                                    bool init_z, Number* z_L, Number* z_U,
00283                                    Index m, bool init_lambda,
00284                                    Number* lambda)
00285 {
00286   // Minimal info is starting values for x, x_init
00287   // Improvement: we can provide starting values for the dual variables if we wish
00288   assert(init_x == true);
00289   assert(init_z == false);
00290   assert(init_lambda == false);
00291
00292   // initialize x to the relaxed solution
00293   for (Index i=0; i<n; ++i)
00294   {
00295     x_init[i] = ipData->relaxedSolution[i];
00296   }
00297
00298   return true;
00299 }
00300
00301 inline
00302 double IARoundingFarNlp::get_f_xl(int i) const
00303 {
00304   return (h0[i] > 0.) ? 0. : -h0[i];
00305 }
00306
00307 inline
00308 double IARoundingFarNlp::get_f_xh(int i) const
00309 {
00310   return (h0[i] > 0.) ? h0[i] : 0.;
00311 }
00312
00313
00314 // returns the value of the objective function
00315 bool IARoundingFarNlp::eval_f(Index n, const Number* x, bool new_x, Number& obj_value)
00316 {
00317   assert(n == data->num_variables());
00318
00319   double obj = 0.;
00320   for (Index i = 0; i<n; ++i)
00321   {
00322     // find which interval we're in
00323     const double xl = ipData->get_xl(i);
00324     const double xh = xl + 1;
00325     const double x_i = x[i];
00326
00327     double obj_i(-1.); // bad value
00328     if (x_i >xh)  // above xh
00329     {
00330       // the slope of the kth interval is k hp[i]
00331       // f = f(xh) + sum_{k=1:kp} k hp + kp_frac (kp+1) hp
00332       //   = f(xh) + kp (kp+1) / 2  hp + kp_frac (kp+1) hp
00333       //   = f(xh) + hp (kp+1) (kp_frac + kp / 2 )
00334       int kp_int;
00335       double kp_frac;
00336       ipData->get_kp(i, x_i, kp_int, kp_frac);
00337       obj_i = get_f_xh(i) + hp[i] * (kp_int + 1) * (kp_frac + ((double) kp_int) / 2.);
00338       assert(obj_i >= 0.);
00339     }
00340     else if (x_i < xl) // below xl
00341     {
00342       // the slope of the kth interval is k hm[i]
00343       // f = f(xl) + sum_{k=1:km} k hm + km_frac (km+1) hm
00344       //   = f(xl) + km (km+1) / 2  hm + km_frac (km+1) hm
00345       //   = f(xl) + hm (km+1) (k_frac + km / 2 )
00346       int km_int;
00347       double km_frac;
00348       ipData->get_km(i, x_i, km_int, km_frac);
00349       obj_i = get_f_xl(i) + fabs(hm[i]) * (km_int + 1) * (km_frac + ((double) km_int) / 2.);
00350       assert(obj_i >= 0.);
00351     }
00352     else // middle
00353     {
00354     // zzyk - testing truncating x within an interval around xl, continuity
00355     /*
00356     static double max_over = 0.;
00357     static double max_under = 0.;
00358
00359     if (x_i < xl)
00360     {
00361       if ( xl - x_i > max_under )
00362       {
00363         max_under = xl - x_i;
00364         printf("max under %g\n", max_under);
00365       }
00366       x_i = xl;
00367     }
00368     else if (x_i > xh)
00369     {
00370       if ( x_i - xh > max_over )
00371       {
00372         max_over = x_i - xh;
00373         printf("max OVER %g\n", max_over);
00374       }
00375       x_i = xh;
00376     } */
00377
00378       obj_i = (h0[i] > 0) ? h0[i] * ( x_i - xl ) : -h0[i] * ( xh - x_i );
00379       assert(obj_i >= 0.);
00380
00381     }
00382     assert(obj_i >= 0.);
00383
00384     obj += obj_i;
00385   }
00386   assert(obj >= 0.);
00387   obj_value = obj;
00388   printf(" %f", obj_value); // debug progress, is this going anywhere?
00389
00390   return true;
00391 }
00392
00395 {
00396   assert(n == data->num_variables());
00397   for (Index i = 0; i<n; ++i)
00398   {
00399     // find which interval we're in
00400     if (x[i] > ipData->get_xh(i)) // above xh
00401     {
00402       // the slope of the kth interval is k hp[i]
00403       int kp_int;
00404       double kp_frac;
00405       ipData->get_kp(i, x[i], kp_int, kp_frac);
00406       grad_f[i] = (1+kp_int) * hp[i];
00407     }
00408     else if (x[i] < ipData->get_xl(i)) // below xl
00409     {
00410       // the slope of the kth interval is k hm[i]
00411       int km_int;
00412       double km_frac;
00413       ipData->get_km(i, x[i], km_int, km_frac);
00414       grad_f[i] = (1+km_int) * hm[i];
00415     }
00416     else // between xl and xh
00417     {
00418       // slope is h0
00420     }
00421   }
00422   return true;
00423 }
00424
00425 // return the value of the constraints: g(x)
00426 bool IARoundingFarNlp::eval_g(Index n, const Number* x, bool new_x, Index m, Number* g)
00427 {
00428   return baseNlp.eval_g(n, x, new_x, m, g);
00429 }
00430
00431 // return the structure or values of the jacobian
00432 bool IARoundingFarNlp::eval_jac_g(Index n, const Number* x, bool new_x,
00433                            Index m, Index nele_jac, Index* iRow, Index *jCol,
00434                            Number* values)
00435 {
00436   return baseNlp.eval_jac_g(n, x, new_x, m, nele_jac, iRow, jCol, values);
00437 }
00438
00439 //return the structure or values of the hessian
00440 bool IARoundingFarNlp::eval_h(Index n, const Number* x, bool new_x,
00441                        Number obj_factor, Index m, const Number* lambda,
00442                        bool new_lambda, Index nele_hess, Index* iRow,
00443                        Index* jCol, Number* values)
00444 {
00445         // because the constraints are linear
00446   // and the objective function is piecewise linear
00447   // the hessian for this problem is actually empty
00448
00449   // to do: may need to fill with zeros so ipopt doesn't think it is globally linear
00450   assert(nele_hess == 0);
00451
00452   // This is a symmetric matrix, fill the lower left triangle only.
00453   if (values == NULL) {
00454     // return the structure.
00455     ;
00456   }
00457   else {
00458     // return the values.
00459     ;
00460   }
00461
00462   return true;
00463 }
00464
00465 void IARoundingFarNlp::finalize_solution(SolverReturn status,
00466                                       Index n, const Number* x, const Number* z_L, const Number* z_U,
00467                                       Index m, const Number* g, const Number* lambda,
00468                                       Number obj_value,
00469                                       const IpoptData* ip_data,
00470                                       IpoptCalculatedQuantities* ip_cq)
00471 {
00472   baseNlp.finalize_solution(status, n, x, z_L, z_U, m, g, lambda, obj_value, ip_data, ip_cq);
00473 }
```