MeshKit  1.0
IASolverBend.cpp
Go to the documentation of this file.
00001 // IASolverBend.cpp
00002 // Interval Assignment for Meshkit
00003 //
00004 #include "IASolverBend.hpp"
00005 #include "IAData.hpp"
00006 #include "IPData.hpp"
00007 #include "IPBend.hpp"
00008 #include "IASolution.hpp"
00009 #include "IABendNlp.hpp"
00010 
00011 #include <stdio.h>
00012 #include <math.h>
00013 #include <limits.h>
00014 
00015 #include "IpIpoptApplication.hpp"
00016 
00017 namespace MeshKit 
00018 {
00019     
00020 IASolverBend::IASolverBend(const IAData * ia_data_ptr, IASolution *relaxed_solution_ptr, 
00021   const bool set_silent) 
00022   : IASolverToolInt(ia_data_ptr, relaxed_solution_ptr, true), evenConstraintsActive(false),
00023     silent(set_silent), debugging(true)
00024 //    silent(set_silent), debugging(false)
00025 { 
00026   ip_data(new IPData);
00027   // initialize copies relaxed solution, then we can overwrite relaxed_solution_pointer with our integer solution 
00028   ip_data()->initialize(relaxed_solution_ptr->x_solution); 
00029 }
00030 
00032 IASolverBend::~IASolverBend() 
00033 {
00034   delete ip_data();
00035 }
00036     
00037 bool IASolverBend::solve_nlp() // IABendNlp *mynlp
00038 {
00039   if (debugging)
00040   {
00041     printf("IASolverBend::solve_nlp() == ");        
00042     printf("BEND problem formulation\n");
00043     printf("Attempting to find a naturally-integer solution by linearizing and bending the objective function at integer values.\n");
00044     printf("x = sum of positive and negative deltas around the floor of the relaxed solution. Deltas are within [0,1]. Deltas are dynamically added. Objective is linear function of weighted deltas, randomized to break ties.\n");
00045   }
00046   
00047   // solver setup  
00048   Ipopt::SmartPtr<Ipopt::IpoptApplication> app = IpoptApplicationFactory();
00049 
00050   //  jac_d_constant
00051   if (evenConstraintsActive && !ia_data()->sumEvenConstraints.empty() )
00052   {
00053     app->Options()->SetStringValue("jac_d_constant", "no"); // default
00054   }
00055   else
00056   {
00057     app->Options()->SetStringValue("jac_d_constant", "yes");
00058   }
00059   // even with the even-constraints, the hessian is constant
00060   app->Options()->SetStringValue("hessian_constant", "yes"); // no by default
00061 
00062 /* try leaving defaults
00063   // convergence parameters
00064   // see $IPOPTDIR/Ipopt/src/Interfaces/IpIpoptApplication.cpp
00065   // our real criteria are: all integer, constraints satisfied. How to test the "all_integer" part?
00066   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
00067   app->Options()->SetNumericValue("max_cpu_time", sqrt( iaData->num_variables() ) ); // max time allowed in seconds
00068   app->Options()->SetIntegerValue("max_iter", 3 * (10 + iaData->num_variables() ) ); // max number of iterations
00069   // app->Options()->SetNumericValue("primal_inf_tol", 1e-2 ); 
00070   app->Options()->SetNumericValue("dual_inf_tol", 1e-2 ); // how close to infeasibility? // was 1e-2
00071   app->Options()->SetNumericValue("constr_viol_tol", 1e-2 ); // by how much can constraints be violated?
00072   app->Options()->SetNumericValue("compl_inf_tol", 1e-6 ); // max norm of complementary condition // was 1e-2
00073   
00074   // second criteria convergence parameters: quit if within this tol for many iterations
00075   // was  app->Options()->SetIntegerValue("acceptable_iter", 4 + sqrt( iaData->num_variables() ) ); //as "tol"
00076   app->Options()->SetNumericValue("acceptable_tol", 1e-6 ); //as "tol" was 1e-1
00077   
00078   app->Options()->SetStringValue("mu_strategy", "adaptive");
00079   // print level 0 to 12, Ipopt default is 5
00080   const int print_level = (silent) ? 0 : 1;  // simple info is 1, debug at other values
00081   app->Options()->SetIntegerValue("print_level", print_level);  
00082   // uncomment next line to write the solution to an output file
00083   // app->Options()->SetStringValue("output_file", "IA.out");  
00084   // The following overwrites the default name (ipopt.opt) of the options file
00085   // app->Options()->SetStringValue("option_file_name", "IA.opt");
00086   
00087   */
00088   const int print_level = 2;  // simple info is 1, debug at other values
00089   app->Options()->SetIntegerValue("print_level", print_level);  
00090   
00091   // Intialize the IpoptApplication and process the options
00092   Ipopt::ApplicationReturnStatus status;
00093   status = app->Initialize();
00094   if (status != Ipopt::Solve_Succeeded) {
00095     if (!silent)
00096       printf("\n\n*** Error during initialization!\n");
00097     return (int) status;
00098   }
00099   
00100   bool try_again = false; // only try again if we didn't converge and want to spend more time
00101   int iter = 0;
00102   
00103   // print();
00104   bool solution_ok = false;
00105   
00106   myianlp->even_constraints_active( evenConstraintsActive );
00107   
00108   do {
00109     if (debugging)
00110     {
00111       print();
00112       printf("%d Bend iteration\n", iter );
00113       // build the hessian, evaluate it and f at the current solution?
00114     }
00115       
00116     // Ask Ipopt to solve the problem
00117     status = app->OptimizeTNLP(myianlp); // the inherited IANlp
00118     
00119     // see /CoinIpopt/build/include/coin/IpReturnCodes_inc.h
00120     /*
00121     Solve_Succeeded=0,
00122     Solved_To_Acceptable_Level=1,
00123     Infeasible_Problem_Detected=2,
00124     Search_Direction_Becomes_Too_Small=3,
00125     Diverging_Iterates=4,
00126     User_Requested_Stop=5,
00127     Feasible_Point_Found=6,
00128     
00129     Maximum_Iterations_Exceeded=-1,
00130     Restoration_Failed=-2,
00131     Error_In_Step_Computation=-3,
00132     Maximum_CpuTime_Exceeded=-4,
00133     Not_Enough_Degrees_Of_Freedom=-10,
00134     Invalid_Problem_Definition=-11,
00135     Invalid_Option=-12,
00136     Invalid_Number_Detected=-13,
00137     
00138     Unrecoverable_Exception=-100,
00139     NonIpopt_Exception_Thrown=-101,
00140     Insufficient_Memory=-102,
00141     Internal_Error=-199
00142      */
00143 
00144     bool solved_full = false;
00145     bool solved_partial = false;
00146     bool solved_failure = false;
00147     bool problem_bad = false;
00148     bool problem_unbounded = false;
00149 
00150     switch (status) {
00151       case Ipopt::Solve_Succeeded:
00152       case Ipopt::Solved_To_Acceptable_Level:
00153       case Ipopt::Feasible_Point_Found:
00154         solved_full = true;
00155         break;
00156       case Ipopt::Maximum_Iterations_Exceeded:
00157       case Ipopt::User_Requested_Stop:
00158       case Ipopt::Maximum_CpuTime_Exceeded:
00159       case Ipopt::Search_Direction_Becomes_Too_Small:
00160         solved_partial = true;
00161         break;
00162       case Ipopt::Infeasible_Problem_Detected:
00163       case Ipopt::Not_Enough_Degrees_Of_Freedom:
00164       case Ipopt::Invalid_Problem_Definition:
00165       case Ipopt::Invalid_Option:
00166       case Ipopt::Invalid_Number_Detected:
00167         problem_bad = true;
00168         break;
00169       case Ipopt::Diverging_Iterates:
00170         problem_unbounded = true;
00171         solved_partial = true;
00172         break;
00173       case Ipopt::Restoration_Failed:
00174       case Ipopt::Error_In_Step_Computation:
00175       case Ipopt::Unrecoverable_Exception:
00176       case Ipopt::NonIpopt_Exception_Thrown:
00177       case Ipopt::Insufficient_Memory:
00178       case Ipopt::Internal_Error:        
00179         solved_failure = true;
00180         break;
00181         
00182       default:
00183         break;
00184     }
00185   
00186     if (!silent)
00187     {
00188       if (solved_full) {
00189         printf("\n\n*** BendNlp solved!\n");
00190       }
00191       else if (solved_partial) {
00192         printf("\n\n*** BendNlp partial success!\n");
00193       }
00194       else {
00195         printf("\n\n*** BendNlp FAILED!\n");
00196       }
00197     }
00198     
00199     if (debugging)
00200     {
00201       printf("\nChecking solution.\n");
00202       bool integer_sat = solution_is_integer(true);
00203       bool even_sat = even_constraints( false, true);
00204       bool equal_sat = equal_constraints( false, true );
00205       printf("Bend solution summary, %s, equal-constraints %s, even-constraints %s.\n", 
00206              integer_sat ? "integer" : "NON-INTEGER",
00207              equal_sat ? "satisfied" : "VIOLATED", 
00208              even_sat ? "satisfied" : "VIOLATED" );
00209     }
00210     try_again = false; 
00211   
00212     if ( solved_full || solved_partial )
00213     {
00214       return true;
00215     }
00216     else
00217     {
00218       // todo: tweak the problem and try again
00219       return false;
00220     }
00221     
00222   } while (try_again);
00223   
00224   return solution_ok;
00225   
00226 }
00227 
00228 bool IASolverBend::round_solution()
00229 {
00230   IASolution nlp_solution;
00231   nlp_solution.x_solution = ia_solution()->x_solution; // vector copy
00232   IASolverToolInt sti( ia_data(), &nlp_solution );
00233   sti.round_solution();
00234   if (debugging)
00235     printf("Checking rounded bend solution.\n");
00236   if (sti.equal_constraints(false, debugging) && sti.even_constraints(false, debugging) )
00237   {
00238   if (debugging)
00239     printf("Rounding worked.\n");
00240     
00241     // rounding was a valid integer solution
00242     ia_solution()->x_solution.swap( nlp_solution.x_solution );
00243     // ia_solution()->obj_value is no longer accurate, as it was for the non-rounded solution
00244     return true;
00245   }
00246   return false;
00247 }
00248 
00249 void IASolverBend::cleanup()
00250 {
00251   // the solution includes the delta values.
00252   // remove those
00253   iaSolution->x_solution.resize( (unsigned) iaData->num_variables());
00254 }
00255 
00256 
00257 // the objective function we should use for deriving the weights
00258 double IASolverBend::f_x_value( double I_i, double x_i ) const
00259 {
00260   return myianlp->f_x_value(I_i, x_i);
00261 }
00262 
00263   
00264 double IASolverBend::fpow(double f) const
00265 {
00266   return f*f*f; // f^3
00267   // todo, experiment with squaring, or less
00268 }
00269   
00270  
00271   /* Idea: a form of IARoundingNlp with larger variable bounds, but still with a natural integer solution.
00272    x in [1..inf]
00273    xr = x optimal relaxed solution with objective function fnlp, see IANlp.xpp 
00274    f is piecewise linear, with corners at integer values. f slopes are unique (we hope)
00275    Slope definitions
00276    for x between xl = floor xr and xh = ceil xr, we use the difference in fnlp between xl and xh
00277    
00278    case A. xr > ceil g, g is goal I[i]
00279    for x above xh, 
00280    let h+ be fnlp ( xh+1 ) - fnlp ( xh )
00281    let kp be the number of intervals x is above xh 
00282    then slope = floor kp * h+
00283    for x below xl, h- = sqrt(11) / 5 h+, and slope = floor km * h-
00284    all this is weighted by some unique weight
00285    
00286    case B. xr in [ floor g, ceil g] 
00287    h+ = fnlp ( xh+1 ) - fnlp ( xh )
00288    h- = fnlp ( xl-1 ) - fnlp ( xl ), not used if xl == 1
00289    
00290    case C. xr < floor g
00291    h- = fnlp ( xl-1 ) - fnlp ( xl )
00292    h+ = sqrt(10)/5 h-
00293    If g < 2, then h- is unused, and h+ = as in case B
00294    
00295    // representation:
00296    h0 is weights 0..n-1
00297    h+ is weights n..2n-1
00298    h- is weights 2n..
00299    
00300    */
00301 
00302   
00303 /* try parabolic constraints instead
00304  
00305 void IASolverBend::add_bend_sum_weights(unsigned int i, const double factor)
00306 {
00307   // scaling issue, 
00308   // in order to overcome the slopes of the integers, the minimum slope should be larger
00309   // than the largest active slope, where active means the weight of the bend delta at the current solution
00310   
00311   IPBend &bend = bendData.bendVec[i];
00312   // designed so xl is the "ideal"
00313   // const double xl = bend.xl; 
00314   
00315   const double s = even_value(i);
00316   const double g = s/2.;
00317 
00318   // pluses
00319   for (int j = 0; j < bend.numDeltaPlus; ++j)
00320   {
00321     double w = bendData.maxActiveVarWeight;
00322     // if current sum is between xl and xl + 1, underweight the first delta
00323     if ( (j == 0) && (g > bend.xl) )
00324     {
00325       double f = ceil(g) - g;
00326       assert( f >= 0.499 ); // xl was chosen to be the closest integer to s/2
00327       w *= f;
00328     }
00329     w *= factor * 2.2 * sqrt( 1.1 * j + 1.1 );    
00330     weights.push_back(w);
00331   }
00332   
00333   // minuses
00334   for (int j = 0; j < bend.numDeltaMinus; ++j)
00335   {
00336     double w = bendData.maxActiveVarWeight;
00337     if ( (j == 0) && (g < bend.xl) )
00338     {
00339       double f = g - floor(g);
00340       assert( f >= 0.499 ); // xl was chosen to be the closest integer to s/2
00341       w *= f;
00342     }
00343     w *= factor * 2.1 * sqrt( 1.02 * j + 1.02 );    
00344     weights.push_back(w);
00345   }
00346 }
00347   
00348  */
00349   
00350   /*
00351   deltapluses
00352   // now modify weights based on tilts
00353   for (std::vector<IPBend::IPTilt>::iterator t = bend.plusTilts.begin();
00354        t != bend.plusTilts.end(); ++t)
00355   {
00356     double tbig = t->first;
00357     double tlit = tbig - 1;
00358     if (tlit < g)
00359       tlit = g;
00360       const double ftlit = f_x_value(g, tlit); 
00361       const double ftbig = f_x_value(g, tbig);
00362       double slope = fpow(ftbig) - fpow(ftlit); 
00363       slope *= t->second;
00364       
00365       if (xbig >= tbig) //if +1
00366       {
00367         assert(w>0.);
00368         assert(slope>0.);
00369         w += fabs(slope);
00370       }
00371   }
00372   for (std::vector<IPBend::IPTilt>::iterator t = bend.minusTilts.begin();
00373        t != bend.minusTilts.end(); ++t)
00374   {
00375     double tbig = t->first;
00376     double tlit = tbig + 1;
00377     if (tlit > g)
00378       tlit = g;
00379       const double ftlit = f_x_value(g, tlit); 
00380       const double ftbig = f_x_value(g, tbig);
00381       double slope = fpow(ftbig) - fpow(ftlit); // always positive
00382       slope *= t->second;
00383       
00384       if (xlit <= tbig)
00385       {
00386         assert(w<0.);
00387         assert(slope>0.);
00388         w -= fabs(slope);
00389       }
00390   }
00391    deltaminuses
00392   // done with tilts
00393    // now modify weights based on tilts
00394    // this is slow and could be sped up by saving prior calculations
00395    for (std::vector<IPBend::IPTilt>::iterator t = bend.plusTilts.begin();
00396    t != bend.plusTilts.end(); ++t)
00397    {
00398    double tbig = t->first;
00399    double tlit = tbig - 1;
00400    if (tlit < g)
00401    tlit = g;
00402    const double ftlit = f_x_value(g, tlit); 
00403    const double ftbig = f_x_value(g, tbig);
00404    double slope = fpow(ftbig) - fpow(ftlit); 
00405    slope *= t->second;
00406    
00407    if (xbig >= tbig)
00408    {
00409    assert(w<0.);
00410    assert(slope>0.);
00411    w -= fabs(slope);
00412    }
00413    }
00414    for (std::vector<IPBend::IPTilt>::iterator t = bend.minusTilts.begin();
00415    t != bend.minusTilts.end(); ++t)
00416    {
00417    double tbig = t->first;
00418    double tlit = tbig + 1;
00419    if (tlit > g)
00420    tlit = g;
00421    const double ftlit = f_x_value(g, tlit); 
00422    const double ftbig = f_x_value(g, tbig);
00423    double slope = fpow(ftbig) - fpow(ftlit); // always positive
00424    slope *= t->second;
00425    
00426    if (xlit <= tbig)
00427    {
00428    assert(w>0.);
00429    assert(slope>0.);
00430    w += fabs(slope);
00431    }
00432    }
00433    // done with tilts
00434    
00435 
00436   */
00437  
00438 void IASolverBend::merge_tilts(IPBend::TiltVec &tilts)
00439 {
00440   if (tilts.size() < 2)
00441     return;
00442   
00443   std::sort( tilts.begin(), tilts.end() ); // sorts by .first
00444 
00445   // copy into a new vec
00446   IPBend::TiltVec new_tilts;
00447   new_tilts.reserve(tilts.size());
00448   
00449   // multiply tilts for the same index together, increasing them faster than additively
00450   for (unsigned int t = 0; t < tilts.size()-1; )
00451   {
00452     unsigned int u = t + 1;
00453     while (u < tilts.size() && tilts[t].first == tilts[u].first)
00454     {
00455       tilts[t].second *= tilts[u].second;
00456       u++;
00457     }
00458     t = u;
00459   }
00460   tilts.swap(new_tilts);  
00461 }
00462   
00463 void IASolverBend::tilt_weight(const IPBend::TiltVec &tilts, const int tilt_direction, const double g, const double xlit, const double xbig, const int delta_direction, double &w)
00464 {
00465   // O( num_deltas * num_tilts)
00466   // this could be sped up some by saving prior calculations or
00467   // by using the sorted order of the tilts to skip some deltas.
00468   for (IPBend::TiltVec::const_iterator t = tilts.begin();
00469        t != tilts.end(); ++t)
00470   {
00471     if (0 && debugging)
00472     {
00473       printf("  tilt (%d) %f at %d\n", tilt_direction, t->second, t->first);
00474     }
00475     double tbig = t->first;                   
00476     double tlit = tbig - tilt_direction;
00477     // if tilt_direction is negative, then tlit index is larger than tbig, 
00478     // but always tlit has smaller f value than tbig
00479     double slope = (tilt_direction > 0) ? raw_weight(g, tlit, tbig) : -raw_weight(g, tbig, tlit);
00480     assert(slope > 0. ); // always defined this way because of tilt_direction
00481     assert(t->second > 0.);
00482     
00483     slope *= t->second;
00484     
00485     // impose an absolute minimum slope of 0.1, to get out of flat regions around g
00486     
00487     if (tilt_direction > 0 && (xbig >= tbig))
00488     {
00489       if (delta_direction > 0)
00490       {
00491         assert(w>0.);
00492         w += fabs(slope);
00493       }
00494       else 
00495       {
00496         assert(w<0.);
00497         w -= fabs(slope); 
00498       }
00499     }
00500     else if (tilt_direction < 0 && (xlit <= tbig))
00501     {
00502       if (delta_direction > 0)
00503       {
00504         assert(w<0.);
00505         w -= fabs(slope);
00506       }          
00507       else {
00508         assert(w>0.);
00509         w += fabs(slope);
00510       }
00511     }
00512   }
00513 }
00514   
00515 double IASolverBend::raw_weight( const double g, double xlit, double xbig)
00516 {
00517   assert(xlit < xbig);
00518   // special handling when crossing goal to avoid zero slope
00519   if ( xlit < g && xbig > g )
00520   {
00521     if ( g - xlit < xbig - g) 
00522       xlit = g;
00523     else
00524       xbig = g;
00525   }
00526   assert( xbig >= 1.);
00527   assert( xlit >= 1.);
00528 
00529   const double flit = f_x_value(g, xlit); 
00530   const double fbig = f_x_value(g, xbig);
00531   const double w = fpow(fbig) - fpow(flit);
00532   return w;
00533 }
00534   
00535 void IASolverBend::add_bend_weights(unsigned int i)
00536 {
00537   // shorthands
00538   IPBend &bend = bendData.bendVec[i];
00539   const double xl = bend.xl;
00540   const double g = iaData->I[i]; // goal
00541   
00542   // current x solution for finding active weight
00543   const double x = iaSolution->x_solution[i]; 
00544 
00545   // pluses
00546   for (int j = 0; j < bend.numDeltaPlus; ++j)
00547   {
00548     double xlit = xl + j;
00549     double xbig = xlit + 1.;
00550     double w = raw_weight( g, xlit, xbig );
00551         
00552     if (0 && debugging)
00553     {
00554       printf("x[%u], g %f, xl %f, dplus[%u] raw_w %f\n", i, g, xl, j, w);
00555     }
00556     // now modify weights based on tilts
00557     tilt_weight(bend.plusTilts, 1, g, xlit, xbig, 1, w);
00558     tilt_weight(bend.minusTilts, -1, g, xlit, xbig, 1, w);
00559     if (0 && debugging)
00560     {
00561       if ( bend.plusTilts.size() || bend.minusTilts.size() )
00562         printf("              tilted_w %f\n", w);
00563     }
00564 
00565     weights.push_back(w);
00566 
00567     // active? or nearly active
00568     if (x <= xbig + 1. && x >= xlit)
00569     {
00570       // biggest?
00571       if (fabs(w) > bendData.maxActiveVarWeight)
00572         bendData.maxActiveVarWeight = fabs(w);
00573     }
00574   }
00575   
00576   // minuses
00577   for (int j = 0; j < bend.numDeltaMinus; ++j)
00578   {
00579     double xbig = xl - j;   
00580     double xlit = xbig - 1.;
00581     assert(xlit >= 1.); // if this fails, then the numDeltaMinus is too large
00582     
00583     double w = - raw_weight( g, xlit, xbig );
00584     
00585     if (0 && debugging)
00586     {
00587       printf("x[%u], g %f, xl %f, dminus[%u] raw_w %f\n", i, g, xl, j, w);
00588     }
00589     // now modify weights based on tilts
00590     tilt_weight(bend.plusTilts, 1, g, xlit, xbig, -1, w);
00591     tilt_weight(bend.minusTilts, -1, g, xlit, xbig, -1, w);    
00592     if (0 && debugging)
00593     {
00594       if ( bend.plusTilts.size() || bend.minusTilts.size() )
00595         printf("             tilted_w %f\n", w);
00596     }
00597     
00598     weights.push_back(w);
00599 
00600     // active? or nearly active
00601     if (x <= xbig && x >= xlit - 1.)
00602     {
00603       // biggest?
00604       if (fabs(w) > bendData.maxActiveVarWeight)
00605         bendData.maxActiveVarWeight = fabs(w);
00606     }
00607   }
00608 
00609 }
00610   
00611 
00612 void IASolverBend::initialize_ip_bends()
00613 {
00614   // problem layout:
00615   // vars:
00616   //   x variables that are supposed to be integer
00617   //   sums that are supposed to be even
00618   //   for i = 0 .. iaData->num_variables()
00619   //     x[i] delta pluses
00620   //     x[i] delta minuses
00621   // 
00622   // constraints
00623   //    base-problem
00624   //      equal
00625   //      even
00626   //    x=deltas, x = xl(const) + deltasplus - deltasminus, <==> x - deltasplus + deltasminus = -xl
00627   //    s=deltas
00628   //
00629   bendData.numSumVars = (int) iaData->sumEvenConstraints.size();
00630   bendData.sumVarStart = iaData->num_variables();
00631   
00632   // loop over the vars, 
00633   // finding upper and lower rounding values
00634   // assign weights
00635   bendData.bendVec.resize( iaData->num_variables() ); // + bendData.numSumVars if we want to do those using bends rather than waves
00636   weights.reserve(bendData.bendVec.size()*4);
00637   
00638   bendData.maxActiveVarWeight = 0.;
00639   
00640   int d_start = iaData->num_variables();
00641   for (int i = 0; i < iaData->num_variables(); ++i)
00642   {
00643     IPBend &bend = bendData.bendVec[i]; // shorthand
00644     bend.deltaIStart = d_start;
00645     
00646     double x = ipData->relaxedSolution[i];
00647     // fix any out-of-bounds roundoff issue
00648     if ( x < 1. ) 
00649       x = 1.;
00650     double xl = bend.xl = floor(x);
00651     assert(xl >= 1.);
00652     // to do, experimenting with starting with +2, -1 
00653 //    if ( x - floor(x) > 0.5 )
00654     {
00655       bend.numDeltaPlus = 2;
00656       bend.numDeltaMinus = 1; 
00657     }
00658     // just one bend, two deltas, is a bad idea, because it can lead to an unbounded objective funtion
00659 //    else
00660 //    {
00661 //      bend.numDeltaPlus = 1;
00662 //      bend.numDeltaMinus = 1;      
00663 //    }
00664 
00665     /*
00666     // debug, test negative deltas by starting with an xl that's too high
00667     x += 2.;
00668     xl = bend.xl = floor(x);
00669     bend.numDeltaPlus = 1;
00670     bend.numDeltaMinus = 1;      
00671     */
00672 
00673     // avoid minus deltas that allow the x solution to be less than 1
00674     if (bend.numDeltaMinus > xl - 1. )
00675       bend.numDeltaMinus = xl - 1;
00676     
00677     // always have at least one deltaMinus and one deltaPlus, so that the full range of x can be represented
00678     assert( ( xl == 1 ) || (bend.numDeltaMinus >= 1) );
00679     assert( bend.numDeltaPlus >= 1 );
00680     
00681     d_start += bend.num_deltas();
00682 
00683     add_bend_weights( i );
00684   }
00685   
00686   /* handle the sum-evens using parabolic constraints intead.
00687 
00688    // initialize all the sum-evens to have no bends - try to enforce those after iteration 1
00689   // as the initial int rounding could change the sum values a lot,
00690   // and unlike the x's there is no intrinsic goal for them.
00691 
00692   for (int i = 0; i < bendData.numSumVars; ++i)
00693   {
00694     IPBend &bend = bendData.bendVec[i + bendData.sumVarStart]; // shorthand
00695     // get current sum-even-value
00696     double s = even_value(i);
00697     bend.xl = floor( s / 2. + 0.5 ); // closest integer to s/2
00698     bend.numDeltaMinus = 1;
00699     bend.numDeltaPlus = 1;
00700     bend.deltaIStart = d_start;
00701 
00702     add_bend_sum_weights( i, 0. );
00703 
00704     d_start += bend.num_deltas();
00705   }
00706    */
00707   
00708   // gather the weights in a vector 
00709   
00710   // uniquify the weights
00711   weights.uniquify(1., 1.e6); // 1.e4 ??
00712   
00713   
00714   // also do that later in a lazy fasion after we see which vars are not integer
00715   
00716   
00717 }
00718   
00719  
00720 bool IASolverBend::update_ip_bends()
00721 {
00722 
00723   // ===============
00724   // check that the structure of the deltas is as expected
00725   // 1, 1, 1, 0.5, 0, 0, 0   or 
00726   // 1, 1, 1,   1, 1, 1, 3
00727   if (debugging)
00728   {
00729     for (int i = 0; i < iaData->num_variables(); ++i)
00730     {
00731       IPBend &bend = bendData.bendVec[i]; // shorthand
00732       
00733       bool plus_deltas = false;
00734       double xprior = 1.;
00735       for (int j = 0; j < bend.numDeltaPlus-1; ++j)
00736       {
00737         const int di = bend.deltaIStart + j;
00738         const double xp = iaSolution->x_solution[ di ];
00739         assert(xp <= xprior + 0.1 );
00740         xprior = xp;
00741         if (xprior < 0.9)
00742           xprior = 0.;
00743         if (xp > 0.1) 
00744           plus_deltas = true;
00745       }
00746       const int diplast = bend.deltaIStart + bend.numDeltaPlus-1;
00747       const double xp = iaSolution->x_solution[ diplast ];
00748       assert( xprior > 0.9 || xp < 1. );
00749       if ( xp > 0.1 )
00750         plus_deltas = true;
00751       
00752       bool minus_deltas = false;
00753       xprior = 1.;
00754       for (int j = 0; j < bend.numDeltaMinus-1; ++j)
00755       {
00756         const int di = bend.deltaIStart + bend.numDeltaPlus + j;
00757         const double xm = iaSolution->x_solution[ di ];
00758         assert( xm <= xprior + 0.1);
00759         xprior = xm;
00760         if (xprior < 0.9)
00761           xprior = 0.;
00762         if (xm > 0.1)
00763           minus_deltas = true;
00764       }
00765       const int dimlast = bend.deltaIStart + bend.numDeltaPlus + bend.numDeltaMinus-1;
00766       const double xm = iaSolution->x_solution[ dimlast ];
00767       assert( xprior > 0.9 || xm < 1. );
00768       if (xm > 0.1)
00769         minus_deltas = true;
00770       assert( ! (plus_deltas && minus_deltas) );
00771     }
00772   }
00773   // ===============
00774   
00775   
00776   // real algorithm
00777 
00778   // ====== new bends
00779   bool new_bend = false; // was at least one new bend added?
00780   bool randomized = false;
00781   
00782   int d_start = iaData->num_variables();
00783   for (int i = 0; i < iaData->num_variables(); ++i)
00784   {
00785     IPBend &bend = bendData.bendVec[i]; // shorthand
00786 
00787     // delta > 1? 
00788     // add more deltas
00789     const int num_delta_plus_old = bend.numDeltaPlus;
00790     const int num_delta_minus_old = bend.numDeltaMinus;
00791     if (bend.numDeltaPlus > 0)
00792     {
00793       const int di = bend.deltaIStart + num_delta_plus_old - 1;
00794       const double xp = iaSolution->x_solution[ di ]; 
00795       if (xp > 1.01)
00796       {
00797         double num_dp_added = floor(xp + 0.1);
00798 
00799         // sanity bound checks
00800         // at most double the number of delta pluses
00801         // there are two to start with, so this adds at least two
00802         double max_add = bend.numDeltaPlus;
00803         if (num_dp_added > max_add)
00804           num_dp_added = max_add;
00805         if (bend.numDeltaPlus > bend.num_deltas_max() - num_dp_added)
00806           num_dp_added = bend.num_deltas_max() - bend.numDeltaPlus;
00807         
00808         bend.numDeltaPlus += num_dp_added;
00809         
00810         if (bend.numDeltaPlus > num_delta_plus_old)
00811           new_bend = true;
00812 
00813         if (debugging)
00814         {
00815           const double xl = bend.xl; // relaxed solution
00816           const double g = iaData->I[i]; // goal
00817           printf("%d x (goal %f relaxed_floor %g) %f delta_plus[%d]=%f -> %g more delta pluses.\n", i, g, xl, iaSolution->x_solution[i], num_delta_plus_old-1, xp, num_dp_added );
00818         }
00819       }
00820     }
00821     if (bend.numDeltaMinus > 0)
00822     {
00823       const int di = bend.deltaIStart + num_delta_plus_old + num_delta_minus_old - 1;
00824       const double xm = iaSolution->x_solution[ di ]; 
00825       if (xm > 1.01 && bend.numDeltaMinus < bend.xl - 1)
00826       {
00827         double num_dm_added = floor(xm + 0.1);
00828         
00829         // sanity bound checks
00830         // at most double +1 the number of delta minuses
00831         // there is one to start with, so this adds at least two
00832         double max_add = bend.numDeltaMinus+1;
00833         if (num_dm_added > max_add)
00834           num_dm_added = max_add;
00835         if (bend.numDeltaMinus > bend.num_deltas_max() - num_dm_added)
00836           num_dm_added = bend.num_deltas_max() - bend.numDeltaMinus;
00837 
00838         // don't let x go below 1
00839         bend.numDeltaMinus += num_dm_added;
00840         if ( bend.numDeltaMinus > bend.xl - 1 )
00841         {
00842           bend.numDeltaMinus = bend.xl - 1;      
00843           num_dm_added = bend.numDeltaMinus - num_delta_minus_old;
00844         }
00845 
00846         if (bend.numDeltaMinus > num_delta_minus_old)
00847           new_bend = true;
00848 
00849         if (debugging)
00850         {
00851           const double xl = bend.xl; // relaxed solution
00852           const double g = iaData->I[i]; // goal
00853           printf("%d x (goal %f relaxed_floor %g) %f delta_minus[%d]=%f -> %g more delta minuses.\n", i, g, xl, iaSolution->x_solution[i], num_delta_minus_old-1, xm, num_dm_added );
00854         }
00855       }
00856     }
00857     bend.deltaIStart = d_start;
00858     
00859     d_start += bend.num_deltas();
00860   }
00861   
00862   // ======= new tilts
00863   // check for fractional deltas 
00864   // only do this if no new bends were added
00865   bool new_tilt = false; // was at least one new tilt added?
00866   if (!new_bend)
00867   {
00868     // the weight indices being current relies on there being no new bends added in the prior loop
00869     IAWeights::iterator wi = weights.begin();
00870 
00871     int num_new_tilts = 0;
00872     for (int i = 0; i < iaData->num_variables(); ++i)
00873     {
00874 
00875       const double x = iaSolution->x_solution[i]; // current solution
00876       if (!is_integer(x))
00877       {
00878         IPBend &bend = bendData.bendVec[i]; // shorthand
00879 
00880         const double g = iaData->I[i]; // goal
00881         
00882         const int xf = floor(x); // int below x
00883         const int xc = xf+1.;  // int above x
00884         
00885         IPBend::IPTilt tilt;
00886 
00887         double r = ((double) rand() / RAND_MAX); // in 0,1
00888         tilt.second =  1.8 + r/2.;
00889         
00890         // if we are on a very flat part of the curve, straddling a goal,
00891         // increase the slope by a lot more
00892         if (fabs(x-g) < 1.1)
00893           tilt.second *= 3.756;
00894         
00895         // xc is farther from the goal, checks for case that the interval straddles the goal
00896         // tilt the positive branch
00897         if (xc - g > g - xf)
00898         {
00899           tilt.first = xc;
00900           tilt.second += x - xf;
00901           bend.plusTilts.push_back(tilt);
00902           
00903           merge_tilts(bend.plusTilts);
00904         }
00905         // tilt the negative branch
00906         else
00907         {
00908           tilt.first = xf;
00909           tilt.second += xc - x;
00910           
00911           bend.minusTilts.push_back(tilt);
00912           std::sort( bend.minusTilts.begin(), bend.minusTilts.end() ); // sorts by .first
00913           merge_tilts(bend.minusTilts);
00914         }
00915         ++num_new_tilts;
00916       }
00917     } // for i
00918     new_tilt = (num_new_tilts > 0);
00919     if (debugging && num_new_tilts)
00920     {
00921       printf("%d stubborn non-integer delta in solution\n", num_new_tilts);
00922     }
00923   }
00924   
00925   // if nothing changed, no need to redo weights, unless randomizing
00926   if ( !(new_bend || new_tilt || randomized) )
00927     return false;
00928       
00929   // generate new deltas and weights
00930   weights.clear();
00931   for (int i = 0; i < iaData->num_variables(); ++i)
00932   {
00933     add_bend_weights(i);
00934   }
00935   
00936     // if delta non-integer, apply tilts
00937     // randomize weight w/ excluded middle
00938 //    for (unsigned int j = 0; j < dp_non_int.size(); ++j)
00939 //    {
00940 //      int k = bend.deltaIStart - iaData->num_variables() + dp_non_int[j];
00941 //      // this might make weights tend to zero. Revisit later
00942 //      weights[ k ] *= 1. + 0.2 * IAWeights::rand_excluded_middle();
00943 //    }
00944 //    for (unsigned int j = 0; j < dm_non_int.size(); ++j)
00945 //    {
00946 //      int k = bend.deltaIStart + bend.numDeltaPlus - iaData->num_variables() + dm_non_int[j];
00947 //      // this might make weights tend to zero. Revisit later
00948 //      weights[ k ] *= 1. + 0.2 * IAWeights::rand_excluded_middle();
00949 //    }
00950 //    
00951     // todo: check that weights are still increasing with increasing k
00952   
00953    weights.uniquify(1., 1e6); // 1e4?
00954 
00955   // return if something changed
00956   return new_bend || new_tilt || randomized;
00957 
00958 }
00959   
00960 bool IASolverBend::solve()
00961 {
00962   if (debugging)
00963   {
00964   }
00965 
00966   myianlp = new IABendNlp(iaData, ipData, &bendData, iaSolution, &weights, silent);
00967   Ipopt::SmartPtr<Ipopt::TNLP> mynlp = myianlp; // Ipopt requires the use of smartptrs!
00968 
00969   // set initial ip bends from relaxed solution
00970   initialize_ip_bends();
00971 
00972   int iter = 0, bend_updates = 0;
00973   bool try_again = true;
00974   bool success = false;
00975   evenConstraintsActive = false;
00976   const int max_last_iter = 4 * ( 2 + iaData->num_variables() );
00977   const int max_first_iter = max_last_iter / 2;
00978   do 
00979   {
00980   
00981     // call the nlp solver
00982     bool solved = solve_nlp();
00983     
00984     try_again = false;
00985     if (solved)
00986     {
00987       
00988       // update bends based on solution
00989       bool changed = update_ip_bends();
00990       if (changed)
00991       {
00992         ++bend_updates;
00993         try_again = true;
00994       }
00995       // try again if sum-evens not already satisfied
00996       if (!even_constraints())
00997         try_again = true;
00998       // if nothing changed, or we've had enough initial iterations,
00999       // then activate the sum-even parabola constraints
01000       if (!changed || (iter > max_first_iter))
01001       {
01002         evenConstraintsActive = true;
01003         // zzyk switch over to the augmented problem, 
01004         // with linear constraints and standard goals for the sum-even variables.
01005         
01006       }
01007     }
01008     // avoid infinite loop if the method isn't working
01009     ++iter;
01010     if ( iter > max_last_iter )
01011       try_again = false;
01012     
01013   }
01014   while (try_again);
01015 
01016   //zzyk debug performance
01017   std::cout << iter << " bend iterations, " << bend_updates << " bend updates" << std::endl;
01018 
01019   cleanup();
01020   
01021   success = solution_is_integer() && all_constraints();
01022   if (success)
01023   {
01024     if (!silent)
01025       printf("IASolverBend produced integer and even solution\n");
01026   }
01027   else
01028   {
01029     return false; 
01030   }
01031   return success;
01032 }
01033 
01034 } // namespace MeshKit
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines