MeshKit  1.0
IASolverInt.cpp
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines