MeshKit
1.0
|
00001 00002 // IASolverInt.cpp 00003 // Interval Assignment for Meshkit 00004 // 00005 #include "meshkit/IASolverInt.hpp" 00006 #include "meshkit/IAData.hpp" 00007 #include "meshkit/IPData.hpp" 00008 #include "meshkit/IASolution.hpp" 00009 #include "meshkit/IARoundingNlp.hpp" 00010 #include "meshkit/IASolverRelaxed.hpp" 00011 #include "meshkit/IASolverBend.hpp" 00012 // #include "meshkit/IAMINlp.hpp" 00013 #include "meshkit/IAIntCosNlp.hpp" 00014 #include "meshkit/IAIntParabolaNlp.hpp" 00015 00016 // #include "meshkit/IAMilp.hpp" // glpk based solution too slow 00017 00018 #include <stdio.h> 00019 #include <math.h> 00020 #include <limits.h> 00021 00022 #include "IpIpoptApplication.hpp" 00023 00024 namespace MeshKit 00025 { 00026 00027 IASolverInt::IASolverInt(const IAData * ia_data_ptr, IASolution *relaxed_solution_ptr, 00028 const bool set_silent) 00029 : IASolverToolInt(ia_data_ptr, relaxed_solution_ptr, true), 00030 silent(false), 00031 // silent(set_silent), 00032 debugging(true) 00033 { 00034 ip_data(new IPData); 00035 // initialize copies relaxed solution, then we can overwrite relaxed_solution_pointer 00036 ip_data()->initialize(relaxed_solution_ptr->x_solution); 00037 } 00038 00040 IASolverInt::~IASolverInt() 00041 { 00042 delete ip_data(); 00043 } 00044 00045 bool IASolverInt::solve_wave_workhorse(IAIntWaveNlp *mynlp) 00046 { 00047 if (debugging) 00048 { 00049 printf("IASolverInt::solve_wave() - "); 00050 printf("Attempting to enforce an integer and even solution to the relaxed NLP by adding constraints that repeat wave-like at each integer lattice point.\n"); 00051 } 00052 00053 // solver setup 00054 Ipopt::SmartPtr<Ipopt::IpoptApplication> app = IpoptApplicationFactory(); 00055 /* try leaving defaults 00056 // convergence parameters 00057 // see $IPOPTDIR/Ipopt/src/Interfaces/IpIpoptApplication.cpp 00058 // our real criteria are: all integer, constraints satisfied. How to test the "all_integer" part? 00059 app->Options()->SetNumericValue("tol", 1e-6); //"converged" if NLP error<this, default is 1e-7. Obj are scaled to be >1, so e-2 is plenty // was 1e-2 00060 app->Options()->SetNumericValue("max_cpu_time", sqrt( iaData->num_variables() ) ); // max time allowed in seconds 00061 app->Options()->SetIntegerValue("max_iter", 3 * (10 + iaData->num_variables() ) ); // max number of iterations 00062 // app->Options()->SetNumericValue("primal_inf_tol", 1e-2 ); 00063 app->Options()->SetNumericValue("dual_inf_tol", 1e-2 ); // how close to infeasibility? // was 1e-2 00064 app->Options()->SetNumericValue("constr_viol_tol", 1e-2 ); // by how much can constraints be violated? 00065 app->Options()->SetNumericValue("compl_inf_tol", 1e-6 ); // max norm of complementary condition // was 1e-2 00066 00067 // second criteria convergence parameters: quit if within this tol for many iterations 00068 // was app->Options()->SetIntegerValue("acceptable_iter", 4 + sqrt( iaData->num_variables() ) ); //as "tol" 00069 app->Options()->SetNumericValue("acceptable_tol", 1e-6 ); //as "tol" was 1e-1 00070 00071 app->Options()->SetStringValue("mu_strategy", "adaptive"); 00072 // print level 0 to 12, Ipopt default is 5 00073 const int print_level = (silent) ? 0 : 1; // simple info is 1, debug at other values 00074 app->Options()->SetIntegerValue("print_level", print_level); 00075 // uncomment next line to write the solution to an output file 00076 // app->Options()->SetStringValue("output_file", "IA.out"); 00077 // The following overwrites the default name (ipopt.opt) of the options file 00078 // app->Options()->SetStringValue("option_file_name", "IA.opt"); 00079 00080 */ 00081 00082 // Intialize the IpoptApplication and process the options 00083 Ipopt::ApplicationReturnStatus status; 00084 status = app->Initialize(); 00085 if (status != Ipopt::Solve_Succeeded) { 00086 if (!silent) 00087 printf("\n\n*** Error during initialization!\n"); 00088 return (int) status; 00089 } 00090 00091 bool try_again = true; 00092 int iter = 0; 00093 00094 // print(); 00095 bool solution_ok = false; 00096 00097 do { 00098 if (debugging) 00099 { 00100 print(); 00101 printf("%d IntWave iteration\n", iter ); 00102 // build the hessian, evaluate it and f at the current solution? 00103 } 00104 00105 // Ask Ipopt to solve the problem 00106 status = app->OptimizeTNLP(mynlp); // the inherited IANlp 00107 00108 // see /CoinIpopt/build/include/coin/IpReturnCodes_inc.h 00109 /* 00110 Solve_Succeeded=0, 00111 Solved_To_Acceptable_Level=1, 00112 Infeasible_Problem_Detected=2, 00113 Search_Direction_Becomes_Too_Small=3, 00114 Diverging_Iterates=4, 00115 User_Requested_Stop=5, 00116 Feasible_Point_Found=6, 00117 00118 Maximum_Iterations_Exceeded=-1, 00119 Restoration_Failed=-2, 00120 Error_In_Step_Computation=-3, 00121 Maximum_CpuTime_Exceeded=-4, 00122 Not_Enough_Degrees_Of_Freedom=-10, 00123 Invalid_Problem_Definition=-11, 00124 Invalid_Option=-12, 00125 Invalid_Number_Detected=-13, 00126 00127 Unrecoverable_Exception=-100, 00128 NonIpopt_Exception_Thrown=-101, 00129 Insufficient_Memory=-102, 00130 Internal_Error=-199 00131 */ 00132 00133 bool solved_full = false; 00134 bool solved_partial = false; 00135 bool solver_failed = false; 00136 bool bad_problem = false; 00137 // setting void just to avoid compiler warning: variable 'solver_failed' set but not used [-Wunused-but-set-variable] 00138 (void) solver_failed; 00139 (void) bad_problem; 00140 switch (status) { 00141 case Ipopt::Solve_Succeeded: 00142 case Ipopt::Solved_To_Acceptable_Level: 00143 case Ipopt::Feasible_Point_Found: 00144 solved_full = true; 00145 break; 00146 case Ipopt::Maximum_Iterations_Exceeded: 00147 case Ipopt::User_Requested_Stop: 00148 case Ipopt::Maximum_CpuTime_Exceeded: 00149 solved_partial = true; 00150 break; 00151 case Ipopt::Infeasible_Problem_Detected: 00152 case Ipopt::Not_Enough_Degrees_Of_Freedom: 00153 case Ipopt::Invalid_Problem_Definition: 00154 case Ipopt::Invalid_Option: 00155 case Ipopt::Invalid_Number_Detected: 00156 bad_problem = true; 00157 break; 00158 case Ipopt::Search_Direction_Becomes_Too_Small: 00159 case Ipopt::Restoration_Failed: 00160 case Ipopt::Diverging_Iterates: 00161 case Ipopt::Error_In_Step_Computation: 00162 case Ipopt::Unrecoverable_Exception: 00163 case Ipopt::NonIpopt_Exception_Thrown: 00164 case Ipopt::Insufficient_Memory: 00165 case Ipopt::Internal_Error: 00166 solver_failed = true; 00167 break; 00168 00169 default: 00170 break; 00171 } 00172 00173 if (!silent) 00174 { 00175 if (solved_full) { 00176 printf("\n\n*** IntWave solved!\n"); 00177 } 00178 else { 00179 printf("\n\n*** IntWave FAILED!\n"); 00180 } 00181 } 00182 00183 if (debugging) 00184 { 00185 printf("\nChecking solution.\n"); 00186 bool integer_sat = solution_is_integer(true); 00187 bool even_sat = even_constraints( false, true); 00188 bool equal_sat = equal_constraints( false, true ); 00189 printf("IntWave solution summary, %s, equal-constraints %s, even-constraints %s.\n", 00190 integer_sat ? "integer" : "NON-INTEGER", 00191 equal_sat ? "satisfied" : "VIOLATED", 00192 even_sat ? "satisfied" : "VIOLATED" ); 00193 if (!integer_sat) 00194 printf("investigate integer neighborhood\n"); 00195 if (!even_sat) 00196 printf("investigate even neighborhood\n"); 00197 if (!equal_sat) 00198 printf("investigate equal neighborhood\n"); 00199 } 00200 00201 00202 IASolution nlp_solution; 00203 nlp_solution.x_solution = ia_solution()->x_solution; // vector copy 00204 IASolverToolInt sti( ia_data(), &nlp_solution ); 00205 sti.round_solution(); 00206 if (debugging) 00207 printf("Checking rounded solution, ignoring even constraints.\n"); 00208 if (sti.equal_constraints(false, debugging)) 00209 { 00210 // also even constraints 00211 if (debugging) 00212 printf("Rounding worked.\n"); 00213 00214 // rounding was a valid integer solution 00215 ia_solution()->x_solution.swap( nlp_solution.x_solution ); 00216 // ia_solution()->obj_value is no longer accurate, as it was for the non-rounded solution 00217 return true; 00218 } 00219 00220 // todo: detect and act 00221 // may have converged to a locally optimal, but non-feasible solution 00222 // if so, try a new starting point 00223 00224 // check solution feasibility, even when not debugging 00225 00226 if ( solved_full || solved_partial ) 00227 { 00228 bool integer_sat = solution_is_integer(false); 00229 bool even_sat = even_constraints( false, false); 00230 bool equal_sat = equal_constraints( false, false ); 00231 if ( integer_sat && even_sat && equal_sat ) 00232 return true; 00233 } 00234 00235 // find out which vars were not integer, 00236 // try moving to a farther starting point resolving 00237 00238 00239 try_again = false; 00240 } while (try_again); 00241 00242 00243 // now 00244 // As the SmartPtrs go out of scope, the reference count 00245 // will be decremented and the objects will automatically 00246 // be deleted. 00247 return solution_ok; 00248 00249 } 00250 00251 00252 bool IASolverInt::solve_round() 00253 { 00254 // set up and call the separate IARoundingNlp, which has a linear objective to get a natural integer solution 00255 // the intuition is this will solve integrality for most variables all at once 00256 00257 if (debugging) 00258 { 00259 printf("IASolverInt::solve_bend_workhorse() - "); 00260 } 00261 00262 00263 // solver setup 00264 Ipopt::SmartPtr<Ipopt::IpoptApplication> app = IpoptApplicationFactory(); 00265 00266 // convergence parameters 00267 // see $IPOPTDIR/Ipopt/src/Interfaces/IpIpoptApplication.cpp 00268 // our real criteria are: all integer, constraints satisfied. How to test the "all_integer" part? 00269 app->Options()->SetNumericValue("tol", 1e-6); //"converged" if NLP error<this, default is 1e-7. Obj are scaled to be >1, so e-2 is plenty // was 1e-2 00270 app->Options()->SetNumericValue("max_cpu_time", sqrt( iaData->num_variables() ) ); // max time allowed in seconds 00271 app->Options()->SetIntegerValue("max_iter", 3 * iaData->num_variables() ); // max number of iterations 00272 // app->Options()->SetNumericValue("primal_inf_tol", 1e-2 ); 00273 app->Options()->SetNumericValue("dual_inf_tol", 1e-6 ); // how close to infeasibility? // was 1e-2 00274 app->Options()->SetNumericValue("constr_viol_tol", 1e-6 ); // by how much can constraints be violated? 00275 app->Options()->SetNumericValue("compl_inf_tol", 1e-6 ); // max norm of complementary condition // was 1e-2 00276 00277 // second criteria convergence parameters: quit if within this tol for many iterations 00278 // was app->Options()->SetIntegerValue("acceptable_iter", 4 + sqrt( iaData->num_variables() ) ); //as "tol" 00279 app->Options()->SetNumericValue("acceptable_tol", 1e-6 ); //as "tol" was 1e-1 00280 00281 app->Options()->SetStringValue("mu_strategy", "adaptive"); 00282 // print level 0 to 12, Ipopt default is 5 00283 const int print_level = (silent) ? 0 : 1; 00284 app->Options()->SetIntegerValue("print_level", print_level); 00285 // uncomment next line to write the solution to an output file 00286 // app->Options()->SetStringValue("output_file", "IA.out"); 00287 // The following overwrites the default name (ipopt.opt) of the options file 00288 // app->Options()->SetStringValue("option_file_name", "IA.opt"); 00289 00290 // Intialize the IpoptApplication and process the options 00291 Ipopt::ApplicationReturnStatus status; 00292 status = app->Initialize(); 00293 if (status != Ipopt::Solve_Succeeded) { 00294 if (!silent) 00295 printf("\n\n*** Error during initialization!\n"); 00296 return (int) status; 00297 } 00298 00299 00300 Ipopt::TNLP *tnlp = NULL; 00301 00302 IARoundingNlp *myianlp = new IARoundingNlp(iaData, ipData, iaSolution, silent); 00303 if (debugging) 00304 { 00305 printf("ROUNDING problem formulation\n"); 00306 printf("Attempting to find a naturally-integer solution by linearizing the objective function.\n"); 00307 printf("Variables are constrained within [floor,ceil] of relaxed solution.\n"); 00308 } 00309 00310 // problem setup 00311 // a couple of different models, simplest to more complex 00312 // IARoundingFarNlp *myianlp = new IARoundingFarNlp(iaData, ipData, this); 00313 // IARoundingFar3StepNlp *myianlp = new IARoundingFar3StepNlp(iaData, ipData, this); // haven't tested this. It compiles and runs but perhaps isn't correct 00314 // IAIntWaveNlp *myianlp = new IAIntWaveNlp(iaData, ipData, this); // haven't tested this. It compiles and runs but perhaps isn't correct 00315 00316 tnlp = myianlp; 00317 Ipopt::SmartPtr<Ipopt::TNLP> mynlp = tnlp; // Ipopt requires the use of smartptrs! 00318 00319 bool try_again = true; 00320 int iter = 0; 00321 do { 00322 printf("%d rounding iteration\n", iter ); 00323 00324 // Ask Ipopt to solve the problem 00325 status = app->OptimizeTNLP(mynlp); // the inherited IANlp 00326 00327 if (!silent) 00328 { 00329 if (status == Ipopt::Solve_Succeeded) { 00330 printf("\n\n*** The problem solved!\n"); 00331 } 00332 else { 00333 printf("\n\n*** The problem FAILED!\n"); 00334 } 00335 } 00336 00337 // The problem should have been feasible, but it is possible that it had no integer solution. 00338 // figure out which variables are still integer 00339 00340 // check solution for integrality and constraint satified 00341 if (debugging) 00342 { 00343 printf("\nChecking Natural (non-rounded) solution.\n"); 00344 bool integer_sat = solution_is_integer(true); 00345 bool even_sat = even_constraints( false, true); 00346 bool equal_sat = equal_constraints( false, true ); 00347 printf("Natural solution summary, %s, equal-constraints %s, even-constraints %s.\n", 00348 integer_sat ? "integer" : "NON-INTEGER", 00349 equal_sat ? "satisfied" : "VIOLATED", 00350 even_sat ? "satisfied" : "VIOLATED" ); 00351 if (!integer_sat) 00352 printf("investigate this\n"); 00353 } 00354 00355 IASolution nlp_solution; 00356 nlp_solution.x_solution = ia_solution()->x_solution; // vector copy 00357 IASolverToolInt sti( ia_data(), &nlp_solution ); 00358 sti.round_solution(); 00359 if (debugging) 00360 printf("Checking rounded solution, ignoring even constraints.\n"); 00361 if (sti.equal_constraints(false, debugging)) 00362 { 00363 // also even constraints 00364 if (debugging) 00365 printf("Rounding worked.\n"); 00366 00367 // rounding was a valid integer solution 00368 ia_solution()->x_solution.swap( nlp_solution.x_solution ); 00369 // ia_solution()->obj_value is no longer accurate, as it was for the non-rounded solution 00370 return true; 00371 } 00372 00373 // find out which vars were not integer, 00374 // try rounding their weights and resolving 00375 // bool int_sat = solution_is_integer(); 00376 ++iter; 00377 try_again = iter < 4 + sqrt(iaData->num_variables()); 00378 if (try_again) 00379 { 00380 if (debugging) 00381 printf("rounding failed, randomizing weights\n"); 00382 00383 myianlp->randomize_weights_of_non_int(); // try again? debug 00384 } 00385 else if (debugging) 00386 printf("giving up on rounding to non-integer solution\n"); 00387 00388 // try_again = false; // debug 00389 } while (try_again); 00390 00391 00392 // todo: update partially-integer solution, perhaps using ipData - figure out how we're going to use it, first, for what structure makes sense. 00393 00394 // As the SmartPtrs go out of scope, the reference count 00395 // will be decremented and the objects will automatically 00396 // be deleted. 00397 return status == Ipopt::Solve_Succeeded; 00398 00399 } 00400 00401 void IASolverInt::cleanup() 00402 { 00403 ; 00404 } 00405 00406 bool IASolverInt::solve_wave(const SolverType solver_type) 00407 { 00408 IAIntWaveNlp *myianlp = NULL; 00409 if (solver_type == COS) 00410 { 00411 if (debugging) printf("Cosine wave.\n"); 00412 myianlp= new IAIntCosNlp(iaData, ipData, iaSolution, silent); 00413 } 00414 else if (solver_type == PARABOLA) 00415 { 00416 if (debugging) printf("Parabola wave.\n"); 00417 myianlp = new IAIntParabolaNlp(iaData, ipData, iaSolution, silent); 00418 } 00419 else 00420 { 00421 if (debugging) printf("Invalid wave type.\n"); 00422 return false; 00423 } 00424 00425 Ipopt::SmartPtr<Ipopt::TNLP> mynlp = myianlp; // Ipopt requires the use of smartptrs! 00426 return solve_wave_workhorse( myianlp ); 00427 } 00428 00429 bool IASolverInt::solve() 00430 { 00431 00432 SolverType solver_type = BEND; // BEND; 00433 00434 // debug, try solve_intwave instead 00435 // longer term, use intwave as a backup when the faster and simpler milp doesn't work. 00436 // unfortunately, it appears to find local minima that are far from optimal, even when starting in a well 00437 00438 bool solved = false; 00439 (void) solved; 00440 switch (solver_type) { 00441 case COS: 00442 case PARABOLA: 00443 solved = solve_wave(solver_type); 00444 break; 00445 case ROUNDING: 00446 solved = solve_round(); 00447 break; 00448 case BEND: 00449 { 00450 IASolverBend sb(iaData, iaSolution, silent); 00451 solved = sb.solve(); 00452 } 00453 break; 00454 default: 00455 solved = false; 00456 break; 00457 } 00458 00459 bool success = solution_is_integer(); 00460 // also check constraints and evenality 00461 if (success) 00462 { 00463 if (!silent) 00464 printf("IASolverInt produced integer solution\n"); 00465 } 00466 else 00467 { 00468 // todo: rather than applying the rounding heuristic, implement a form of IARoundingNlp with larger variable bounds, but still with a natural integer solution, by extending x to also depend on a delta_plus and delta_minus extending x beyond xl and xl+1, i.e. x = xl (const) + xh (0-1 var) + delta_plus - delta_minus. With linear objective with weight for xh as before, but weight for delta_plus to be f( xl + 2 ) - f (xl + 1), delta_minus f( xl - 2) - f(xl -1) 00469 return false; // debug; 00470 // solve_rounding_heuristic(); 00471 // success = solution_is_integer(); 00472 } 00473 return success; 00474 } 00475 00476 } // namespace MeshKit