MeshKit
1.0
|
00001 // IARoundingFar3StepNlp.cpp 00002 // Interval Assignment for Meshkit 00003 // 00004 // Adapted from 00005 // Copyright (C) 2005, 2006 International Business Machines and others. 00006 // All Rights Reserved. 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 "meshkit/IARoundingFar3StepNlp.hpp" 00014 #include "meshkit/IARoundingNlp.hpp" 00015 #include "meshkit/IAData.hpp" 00016 #include "meshkit/IPData.hpp" 00017 #include "meshkit/IASolution.hpp" 00018 #include "meshkit/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 = (1 + floor(kp)/2) * 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+ = hp is weights n..2n-1 00061 h- = hm is weights 2n.. 00062 00063 // variable layout 00064 x01 is variables n..2n-1 : x01_start 00065 xp is variables 2n..3n-1 : xp_start 00066 xm is varaibles 3n..4n-1 : xm_start 00067 00068 // constrataint layout 00069 sum-equal 00070 sum-even : sum_even_start 00071 x = xl + x01 + xp - xm : x_constraint_start 00072 or g(j)= x - x01 - xp + xm = xl 00073 */ 00074 00075 00076 00077 // the objective function we should use for deriving the weights 00078 double IARoundingFar3StepNlp::f_x_value( double I_i, double x_i ) const 00079 { 00080 // to do revise 00081 return x_i > I_i ? 00082 (x_i - I_i) / I_i : 00083 1.103402234045 * (I_i - x_i) / x_i; 00084 } 00085 00086 // destructor 00087 IARoundingFar3StepNlp::~IARoundingFar3StepNlp() 00088 { 00089 data = NULL; 00090 ipData = NULL; 00091 solution = NULL; 00092 } 00093 00094 // constructor 00095 IARoundingFar3StepNlp::IARoundingFar3StepNlp(const IAData *data_ptr, const IPData *ip_data_ptr, IASolution *solution_ptr): 00096 data(data_ptr), ipData(ip_data_ptr), solution(solution_ptr), baseNlp(data_ptr, solution_ptr), 00097 x01_start( data_ptr->num_variables() ), 00098 xp_start( 2 * data_ptr->num_variables() ), 00099 xm_start( 3 * data_ptr->num_variables() ), 00100 base_n ( data_ptr->num_variables() ), 00101 base_m ( (int) ( data_ptr->constraints.size() + data->sumEvenConstraints.size() ) ), 00102 problem_n( (int) 4 * data_ptr->num_variables() ), 00103 problem_m( (int) (data_ptr->constraints.size() + data_ptr->sumEvenConstraints.size() + data_ptr->num_variables())), 00104 sum_even_start( (int) data_ptr->constraints.size()), 00105 x_constraint_start( (int) (data_ptr->constraints.size() + data_ptr->sumEvenConstraints.size())), 00106 hess_option(ZERO), 00107 debugging(true), verbose(true) // true 00108 { 00109 printf("\nIARoundingFar3StepNlp Problem:\n"); 00110 00111 00112 // weights for function value 00113 h0.resize(data->num_variables()); 00114 hp.resize(data->num_variables()); 00115 hm.resize(data->num_variables()); 00116 for (int i = 0; i < data->num_variables(); ++i) 00117 { 00118 const double x = ipData->relaxedSolution[i]; 00119 const double xl = floor(x); 00120 const double xh = xl + 1; 00121 const double g = data->I[i]; // goal 00122 00123 const double fl = f_x_value(g, xl); 00124 const double fh = f_x_value(g, xh); 00125 const double w = fh - fl; 00126 h0[i] = w; 00127 00128 // use sqrt(3) to ensure hp and hm is larger than h0 00129 const double fp = sqrt(3) * (f_x_value(g,xh+1) - f_x_value(g,xh)); 00130 double fm = 0.; 00131 00132 if (xl > 1) 00133 { 00134 fm = sqrt(5) * (f_x_value(g,xl) - f_x_value(g,xl-1)); 00135 } 00136 else // if (xl == 1) 00137 { 00138 // unused anyway, assign it something that won't screw up the distribution 00139 fm = - sqrt(5) * fabs(f_x_value(g,2) - f_x_value(g,1)); 00140 } 00141 00142 // case A 00143 if ( x > ceil(g) ) 00144 { 00145 hp[i] = fp; 00146 hm[i] = -fp * sqrt(11) / 5.; 00147 assert( fabs(hm[i]) < fabs(hp[i]) ); 00148 } 00149 // case B 00150 else if ( x >= floor(g) ) 00151 { 00152 hp[i] = fp; 00153 hm[i] = fm; 00154 } 00155 // case C 00156 else // x < floor(g) 00157 { 00158 hm[i] = fm; 00159 hp[i] = -fm * sqrt(7) / 3.; 00160 assert( fabs(hm[i]) > fabs(hp[i]) ); 00161 } 00162 00163 // invariants for all cases, to produce a super-linear preference for xl or xh 00164 assert( hp[i] > 0 ); 00165 assert( hp[i] > h0[i] ); 00166 assert( hm[i] < 0 ); 00167 assert( hm[i] < h0[i] ); 00168 00169 /* 00170 //zzyk experiment with limited f' discontinuity 00171 // this makes hp < 0 and hm > 0 sometimes, so watch the other asserts 00172 if ( h0[i] > 0 ) 00173 { 00174 hp[i] = h0[i]*2.; 00175 hm[i] = h0[i]*0.5; 00176 } 00177 else 00178 { 00179 hp[i] = h0[i]*0.5; 00180 hm[i] = h0[i]*2.; 00181 } 00182 */ 00183 00184 if (0 && debugging) 00185 printf(": %f %f %f", h0[i], hp[i], hm[i]); // 0th+/i, first should be positive, second negative 00186 } 00187 00188 // uniquify and scale the weights = slopes 00189 const double h0_lo = 1; 00190 const double h0_hi = h0_lo * (10 + 2 * data->num_variables()); 00191 // uniquify preserving h0 < hm, hp 00192 if (0) 00193 { 00194 // todo experiment with ranges 00195 // want the largest h0 to be less then the smallest hp or hm, in absolute value 00196 // so that a simple rounding from the relaxed solution is optimal, if feasible 00197 // 1.e4? 1.e3 makes the jumps in values wash out for the big chain problem, need some jumps? 00198 // idea, dynamically scale this based on the problem size, h0_hi = 2*num_variables; 00199 const double hpm_lo = h0_hi + 1.; 00200 const double hpm_hi = 1.e5; 00201 h0.uniquify(h0_lo, h0_hi); 00202 00203 // uniquify and scale hp hm to come after h0 00204 IAWeights hpm; 00205 hpm.weightvec = hp.weightvec; // hpm = hp 00206 hpm.weightvec.insert( hpm.weightvec.end(), hm.weightvec.begin(), hm.weightvec.end() ); // hmp += hm 00207 hpm.uniquify_weights(hpm_lo, hpm_hi); 00208 hp.weightvec.assign(hpm.weightvec.begin(), hpm.weightvec.begin()+hp.size()); // extract hp 00209 hm.weightvec.assign(hpm.weightvec.begin()+hp.weightvec.size(), hpm.weightvec.end()); // extract hm 00210 } 00211 // uniquify lumping h0 with hm and hp 00212 else { 00213 h0.uniquify(h0, h0_lo, h0_hi); 00214 00215 // uniquify and scale hp hm to come after h0 00216 IAWeights h0pm( h0 ); // h0pm = h0 00217 h0pm.insert( h0pm.end(), hp.begin(), hp.end() ); // h0pm += hp 00218 h0pm.insert( h0pm.end(), hm.begin(), hm.end() ); // h0pm += hm 00219 h0pm.uniquify_weights(h0_lo, h0_hi); 00220 const size_t sz = data->num_variables(); 00221 h0.assign(h0pm.begin(), h0pm.begin()+sz); // extract h0 00222 hp.assign(h0pm.begin()+sz, h0pm.begin()+2*sz); // extract hp 00223 hm.assign(h0pm.begin()+2*sz, h0pm.end()); // extract hm 00224 00225 } 00226 00227 if (debugging) 00228 { 00229 printf("\n"); 00230 for (int i = 0; i < data->num_variables(); ++i) 00231 { 00232 const double x = ipData->relaxedSolution[i]; 00233 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]); 00234 } 00235 } 00236 } 00237 00238 bool IARoundingFar3StepNlp::randomize_weights_of_non_int() 00239 { 00240 // either they all will be true or they all will be false, because 00241 // true means a solution value was non-integer 00242 IASolverToolInt sti(data, solution); 00243 bool changed = 00244 randomize_weights_of_non_int( h0, 0.03124234 ) && 00245 randomize_weights_of_non_int( hm, 0.02894834 ) && 00246 randomize_weights_of_non_int( hp, 0.02745675 ); 00247 00248 return changed; 00249 } 00250 00251 00252 // returns the size of the problem 00253 bool IARoundingFar3StepNlp::get_nlp_info(Index& n, Index& m, Index& nnz_jac_g, 00254 Index& nnz_h_lag, IndexStyleEnum& index_style) 00255 { 00256 bool base_ok = baseNlp.get_nlp_info(n, m, nnz_jac_g, nnz_h_lag, index_style); 00257 00258 m = problem_m; 00259 n = problem_n; 00260 00261 // nnz_jac_g == nele_jac in eval_jac_g 00262 // four coefficients for each g() = x - x01 - xp + xm = xl const constraint 00263 nnz_jac_g += 4 * base_n; 00264 00265 // nnz_h_lag == nele_hess in eval_h 00266 if (hess_option == ZERO) 00267 nnz_h_lag = 0.; 00268 else if (hess_option == ROUNDED) 00269 nnz_h_lag = 2 * data->num_variables(); // one for each xp, xm 00270 00271 return true && base_ok; 00272 // need to change this if there are more variables, such as delta-minuses 00273 } 00274 00275 // returns the variable bounds 00276 bool IARoundingFar3StepNlp::get_bounds_info(Index n, Number* x_l, Number* x_u, 00277 Index m, Number* g_l, Number* g_u) 00278 { 00279 const bool base_ok = baseNlp.get_bounds_info(base_n, x_l, x_u, base_m, g_l, g_u); 00280 00281 for (Index i=0; i<data->num_variables(); ++i) 00282 { 00283 // x01 00284 const int x01i = x01_start + i; 00285 x_l[x01i] = 0; 00286 x_u[x01i] = 1; 00287 00288 // xp 00289 const int xpi = xp_start + i; 00290 x_l[xpi] = 0; 00291 x_u[xpi] = MESHKIT_IA_upperUnbound; 00292 00293 // xm 00294 const int xmi = xm_start + i; 00295 x_l[xmi] = 0; 00296 x_u[xmi] = ipData->get_xl(i) - 1; 00297 assert( x_u[xmi] >= 0. ); 00298 } 00299 00300 // x = xl + x01 + xp - xm : x_constraint_start 00301 // or g(j)= x - x01 - xp + xm = xl 00302 for (Index j=0; j<data->num_variables(); ++j) 00303 { 00304 const int xcj = x_constraint_start + j; 00305 const int xl = ipData->get_xl(j); 00306 g_l[xcj] = xl; 00307 g_u[xcj] = xl; 00308 } 00309 00310 return base_ok && true; 00311 } 00312 00313 // returns the initial point for the problem 00314 bool IARoundingFar3StepNlp::get_starting_point(Index n, bool init_x, Number* x_init, 00315 bool init_z, Number* z_L, Number* z_U, 00316 Index m, bool init_lambda, 00317 Number* lambda) 00318 { 00319 // Minimal info is starting values for x, x_init 00320 // Improvement: we can provide starting values for the dual variables if we wish 00321 assert(init_x == true); 00322 assert(init_z == false); 00323 assert(init_lambda == false); 00324 00325 // initialize x to the relaxed solution 00326 for (Index i=0; i<base_n; ++i) 00327 { 00328 const double xr = ipData->relaxedSolution[i]; 00329 x_init[i] = xr; 00330 x_init[x01_start + i] = xr - floor(xr); 00331 x_init[xp_start + i] = 0.; 00332 x_init[xm_start + i] = 0.; 00333 } 00334 00335 return true; 00336 } 00337 00338 inline 00339 double IARoundingFar3StepNlp::get_f_xl(int i) const 00340 { 00341 return (h0[i] > 0.) ? 0. : -h0[i]; 00342 } 00343 00344 inline 00345 double IARoundingFar3StepNlp::get_f_xh(int i) const 00346 { 00347 return (h0[i] > 0.) ? h0[i] : 0.; 00348 } 00349 00350 00351 // returns the value of the objective function 00352 bool IARoundingFar3StepNlp::eval_f(Index n, const Number* x, bool new_x, Number& obj_value) 00353 { 00354 assert(n == problem_n); 00355 00356 double obj = 0.; 00357 for (Index i = 0; i<base_n; ++i) 00358 { 00359 // x itself contributes nothing, just its components x01, xp, xm 00360 00361 // xp 00362 { 00363 const double xpv = x[xp_start + i]; 00364 int kp_int; 00365 double kp_frac; 00366 IPData::get_frac( xpv, kp_int, kp_frac ); 00367 const double objp = hp[i] * ( kp_int * ( 1. + (kp_int + 1.) / 4. ) + kp_frac * ( 1. + kp_int / 2. ) ); 00368 assert( xpv >= 0. ); 00369 assert(objp >= 0.); 00370 obj += objp; 00371 } 00372 00373 // xm 00374 { 00375 const double xmv = x[xm_start + i]; 00376 int km_int; 00377 double km_frac; 00378 IPData::get_frac( xmv, km_int, km_frac ); 00379 const double objm = -hm[i] * ( km_int * ( 1. + (km_int + 1.) / 4. ) + km_frac * ( 1. + km_int / 2. ) ); 00380 assert( xmv >= 0. ); 00381 assert(objm >= 0.); 00382 obj += objm; 00383 } 00384 00385 // x01 00386 { 00387 const double x01v = x[x01_start + i]; 00388 const double obj01 = (h0[i] > 0) ? h0[i] * x01v : h0[i] * ( x01v - 1. ); 00389 assert(x01v >= 0.); 00390 assert(x01v <= 1.); 00391 assert(obj01 >= 0.); 00392 obj += obj01; 00393 } 00394 } 00395 assert(obj >= 0.); 00396 obj_value = obj; 00397 printf(" %f", obj_value); // debug progress, is this going down? 00398 00399 return true; 00400 } 00401 00402 // return the gradient of the objective function grad_{x} f(x) 00403 bool IARoundingFar3StepNlp::eval_grad_f(Index n, const Number* x, bool new_x, Number* grad_f) 00404 { 00405 assert(n == problem_n); 00406 for (Index i = 0; i<base_n; ++i) 00407 { 00408 // x 00409 grad_f[i] = 0; 00410 00411 // xp 00412 { 00413 const double xpv = x[xp_start + i]; 00414 int kp_int; 00415 double kp_frac; 00416 IPData::get_frac( xpv, kp_int, kp_frac ); 00417 grad_f[xp_start+i] = hp[i] * ( 1. + kp_int / 2. ); 00418 } 00419 00420 // xm 00421 { 00422 const double xmv = x[xm_start + i]; 00423 int km_int; 00424 double km_frac; 00425 IPData::get_frac( xmv, km_int, km_frac ); 00426 grad_f[xm_start+i] = -hm[i] * ( 1. + km_int / 2. ); 00427 } 00428 00429 // x01 00430 grad_f[x01_start+i] = h0[i]; 00431 } 00432 00433 return true; 00434 } 00435 00436 // return the value of the constraints: g(x) 00437 bool IARoundingFar3StepNlp::eval_g(Index n, const Number* x, bool new_x, Index m, Number* g) 00438 { 00439 const bool base_ok = baseNlp.eval_g(base_n, x, new_x, base_m, g); 00440 00441 // x_constraints 00442 for (int i=0; i<base_n; ++i) 00443 { 00444 // g(k)= x - x01 - xp + xm = xl 00445 const double xv = x[i]; 00446 const double x01v = x[x01_start+i]; 00447 const double xpv = x[xp_start+i]; 00448 const double xmv = x[xm_start+i]; 00449 const double g_k = xv - x01v - xpv + xmv; 00450 const int k = x_constraint_start + i; 00451 g[k] = g_k; 00452 } 00453 00454 return base_ok && true; 00455 } 00456 00457 // return the structure or values of the jacobian 00458 bool IARoundingFar3StepNlp::eval_jac_g(Index n, const Number* x, bool new_x, 00459 Index m, Index nele_jac, Index* iRow, Index *jCol, 00460 Number* values) 00461 { 00462 int base_nele_jac = baseNlp.get_neleJac(); 00463 bool base_ok = baseNlp.eval_jac_g(base_n, x, new_x, base_m, base_nele_jac, iRow, jCol, values); 00464 base_nele_jac = baseNlp.get_neleJac(); 00465 int k = base_nele_jac; 00466 00467 if (values == NULL) 00468 { 00469 // return the structure of the jacobian 00470 00471 // x_constraints 00472 for (int i=0; i<base_n; ++i) 00473 { 00474 // g(k)= x - x01 - xp + xm = xl const 00475 int xi = i + x_constraint_start; 00476 iRow[k] = xi; 00477 jCol[k] = i; // x 00478 ++k; 00479 iRow[k] = xi; 00480 jCol[k] = x01_start + i; // x01 00481 ++k; 00482 iRow[k] = xi; 00483 jCol[k] = xp_start + i; // xp 00484 ++k; 00485 iRow[k] = xi; 00486 jCol[k] = xm_start + i; // xm 00487 ++k; 00488 } 00489 } 00490 else 00491 { 00492 // return the values of the jacobian of the constraints 00493 00494 // x_constraints 00495 for (int i=0; i<base_n; ++i) 00496 { 00497 // g(k)= x - x01 - xp + xm = xl const 00498 values[k++] = 1; // x 00499 values[k++] = -1; // x01 00500 values[k++] = -1; // xp 00501 values[k++] = 1; // xm 00502 } 00503 00504 } 00505 return base_ok && true; 00506 } 00507 00508 //return the structure or values of the hessian 00509 bool IARoundingFar3StepNlp::eval_h(Index n, const Number* x, bool new_x, 00510 Number obj_factor, Index m, const Number* lambda, 00511 bool new_lambda, Index nele_hess, Index* iRow, 00512 Index* jCol, Number* values) 00513 { 00514 // because the constraints are linear 00515 // the hessian for them is actually empty 00516 00517 // get_nlp_info specified the number of non-zeroes, nele_hess 00518 00519 00520 // This is a symmetric matrix, fill the lower left triangle only. 00521 if (values == NULL) { 00522 // return the structure. 00523 if (hess_option == ZERO) 00524 { 00525 ; // empty 00526 } 00527 else if (hess_option == ROUNDED) 00528 { 00529 // Since the objective function is separable, only the diagonal is non-zero 00530 int k = 0; 00531 for ( int i = 0; i < base_n; ++i ) 00532 { 00533 iRow[k] = xp_start + i; 00534 jCol[k] = xp_start + i; 00535 ++k; 00536 } 00537 for ( int i = 0; i < base_n; ++i ) 00538 { 00539 iRow[k] = xm_start + i; 00540 jCol[k] = xm_start + i; 00541 ++k; 00542 } 00543 } 00544 } 00545 else { 00546 // return the values. 00547 int k = 0; 00548 if (hess_option == ZERO) 00549 { 00550 ; // empty 00551 } 00552 else if (hess_option == ROUNDED) 00553 { 00554 for ( int i = 0; i < base_n; ++i ) 00555 { 00556 values[k] = hp[i] / 2.; 00557 // but linearly fade to zero towards xh 00558 const double xp = x[xp_start + i]; 00559 if (xp < 0.5) 00560 values[k] *= 2. * xp; 00561 00562 ++k; 00563 } 00564 for ( int i = 0; i < base_n; ++i ) 00565 { 00566 values[k] = -hm[i] / 2.; 00567 const double xm = x[xm_start + i]; 00568 if (xm < 0.5) 00569 values[k] *= 2. * xm; 00570 00571 ++k; 00572 } 00573 00574 } 00575 assert( k == nele_hess ); 00576 } 00577 00578 00579 return true; 00580 } 00581 00582 void IARoundingFar3StepNlp::finalize_solution(SolverReturn status, 00583 Index n, const Number* x, const Number* z_L, const Number* z_U, 00584 Index m, const Number* g, const Number* lambda, 00585 Number obj_value, 00586 const IpoptData* ip_data, 00587 IpoptCalculatedQuantities* ip_cq) 00588 { 00589 baseNlp.finalize_solution(status, base_n, x, z_L, z_U, base_m, g, lambda, obj_value, ip_data, ip_cq); 00590 // ignore the other constraints and variables, as they were just bookkeepings. 00591 }