メインコンテンツへスキップ

Get multiply feasible solutions from solution pool for mip

回答済み

コメント

14件のコメント

  • 正式なコメント
    Simranjit Kaur
    • Gurobi Staff
    This post is more than two years old. Some information may not be up to date. For current information, please check the Gurobi Documentation or Knowledge Base. If you need more help, please create a new post in the community forum, or try Gurobot, our chatbot interface offering instant, expert-level support.
  • Marika Karbstein
    • Gurobi Staff

    Hi,
    All solutions in the solution pool should be feasible solutions.
    Could you share a minimal reproducible example? 
    Best, Marika

    0
  • Dong Han
    • Gurobi-versary
    • Curious
    • Conversationalist

    Hi Marika, Thank you very much for you reply. I just found I could get optimal solution which is definitely feasible when I set PoolSearchMode by default and only get one solution. But, when I want to get more feasible solution with default PoolSearchMode, I found some solution is infeasible, it seems failed to add lazy constraints(by callback). I just don't know why sometimes adding lazy constraints works, sometimes not.(I set GRB_IntParam_LazyConstraints to 1). Since I revised some part of code, I will try to provide an example.

    if (len < n) {
    // cout << "XXXX len = " << len << endl;
    // for (int i = 0; i < len; i++)
    // cout << tour[i] << " ";
    // cout << endl;
    // for (int i = 0; i < n; i++){
    // for (int j = i+1; j < n; j++) {
    // if (x[i][j] > 0.5)
    // cout << "x:" << i << " " << j << endl;
    // if (x_bar[i][j] > 0.5)
    // cout << "x_bar:" << i << " " << j << endl;
    // }
    // }
    // Add subtour elimination constraint
    GRBLinExpr expr = 0;
    //cout << "starting building constraints.." << endl;
    for(int i =0; i < len-1; i++){
    for(int j = i+1; j < len; j++){
    expr += vars[tour[i]][tour[j]];
    expr += vars_bar[tour[i]][tour[j]];
    }
    }
    //cout << "finishing building constraints.." << endl;
    addLazy(expr <= len-1);
    //cout << "finishing add lazy constraints.." << endl;
    }
    0
  • Marika Karbstein
    • Gurobi Staff

    This is not a reproducible example.

    If I understand you correctly you started with the Gurobi example tsp_c++.cpp. But why do you change something in the code if you want to get more feasible solutions with default PoolSearchMode? 

    If I run the example tsp_c++.cpp with value 12, Gurobi finds 2 solutions:

    Solution count 2: 3.41529 6.06623

    Both solutions are feasible. If you want Gurobi to find more solutions, you need to change the parameter PoolSearchMode to 1 or 2 (and maybe also adapt PoolSolutions).
    I do not see where you get infeasible solutions.

    0
  • Dong Han
    • Gurobi-versary
    • Curious
    • Conversationalist

    Yes. I could also get the results you shown here. But if I want to print these two solutions with GRB_IntParam_SolutionNumber, sometimes the sub-optimal solution is infeasible, which means it has subtour, like "0-1-3-2" and "4-6-7-5" if we have 8 nodes. Do you mean I have to set PoolSearchMode to 1 or 2 to get feasible sub-optimal solution?

    0
  • Dong Han
    • Gurobi-versary
    • Curious
    • Conversationalist
    Since I need to revised tsp_c++ for my own project, I just show you the part of code and infeasible result I got. For example, I have 22 node, but I got a tour with only 5 nodes.
     
    int SolCount = model.get(GRB_IntAttr_SolCount);

    if(SolCount >0){

    double poolobj =0.0;

    for(int i =0; i < SolCount; i++){

    model.set(GRB_IntParam_SolutionNumber, i);

    poolobj = model.get(GRB_DoubleAttr_PoolObjVal);

    double**sol =newdouble*[n];

    double**sol_bar =newdouble*[n];

    for(int i =0; i < n; i++)

    {

    sol[i]=model.get(GRB_DoubleAttr_Xn,vars[i], n);

    sol_bar[i]=model.get(GRB_DoubleAttr_Xn,vars_bar[i], n);

    }

    int* tour =newint[n];

    int len;

    findsubtour(n, sol, sol_bar,&len, tour);

    if(len < n){

    cout << "len = " << len << endl;

    cout << "tour : " << " ";

    for(int i =0; i < len; i++){

    cout << tour[i] << " ";

    }

    cout << endl;

    The result is as following.

    len = 5

    tour :  3 4 11 16 19

    sol:0 14

    sol:0 17

    sol:1 2

    sol:1 6

    sol:2 5

    sol:3 4

    sol_bar:3 19

    sol:4 11

    sol:5 7

    sol:6 10

    sol:7 9

    sol:8 10

    sol_bar:8 13

    sol_bar:9 17

    sol_bar:11 16

    sol_bar:12 13

    sol:12 15

    sol:14 21

    sol:15 18

    sol:16 19

    sol:18 20

    sol:20 21
    0
  • Marika Karbstein
    • Gurobi Staff

    This is still not enough information to help you. Your problem is probably not defined properly.
    Could you explain a bit more about what you exactly want to solve?
    Why do you have variables vars and vars_bar? What does vars_bar represent?
    What does the callback to add the sub-tour elimination constraints look like?
    What does the output "sol: 0 14" or "sol_bar: 3 19" mean?

    0
  • Dong Han
    • Gurobi-versary
    • Curious
    • Conversationalist

    Thank you very much for your reply! Since I revised tsp_c++ to fit my model, vars and vars_bar are both decision variables. For example, as for 0 14, vars[0][14] + vars_bar[0][14] = 1, which means if we travel from 0 to 14, we could make vars[0][14] = 1 or vars_bar[0][14] = 1. As for the solution part, sol: 0 14 means the optimal solution told us that we travel from 0 to 14 by vars[0][14] = 1 and we travel from 3 to 19 by vars_bar[3][19] = 1. From above result, when I get this suboptimal solution, this suboptimal solution has a subtour 3 4 11 16 19, which is infeasible because subtour solution should be eliminated by lazy constraints.  So I think the callback function didn't work for this suboptimal, but I have no idea how to fix it. Here is the callback part. Thank you.

    void callback() {

    try{

    if(where == GRB_CB_MIPSOL){

    // Found an integer feasible solution - does it visit every node?

    double**x =newdouble*[n];

    double**x_bar =newdouble*[n];

    int*tour =newint[n];

    int len;

    for(int i =0; i < n; i++){

    x[i]=getSolution(vars[i], n);

    x_bar[i]=getSolution(vars_bar[i],n);

    }

    findsubtour(n, x, x_bar,&len, tour);

    if(len < n){

    // Add subtour elimination constraint

    GRBLinExpr expr = 0;

    for(int i =0; i < len-1; i++){

    for(int j = i+1; j < len; j++){

    expr += vars[tour[i]][tour[j]];

    expr += vars_bar[tour[i]][tour[j]];

    }

    }

    addLazy(expr <= len-1);

    }

    for(int i =0; i < n; i++)

    {

    delete[]x[i];

    delete[]x_bar[i];

    }

    delete[] x;

    delete[] x_bar;

    delete[] tour;




    }

    }catch(GRBException e){

    cout << "Error number: " << e.getErrorCode() << endl;

    cout << e.getMessage() << endl;

    }catch(...){

    cout << "Error during callback" << endl;

    }

    }
    0
  • Marika Karbstein
    • Gurobi Staff

    I cannot reproduce the issue.

    You probably need to share the complete reproducible example.

    0
  • Dong Han
    • Gurobi-versary
    • Curious
    • Conversationalist

    Hi, Here is my code. The main function of this project is to find several routes that start from 0 and visited several nodes and finally go back to 0. For example, if I have 0 1 2 3 4 5 6 7 8, and I would like to get the results that looks like 0-1-2-4-5-0 and 0-3-8-7-6-0. And I do not want to get subtour like 3-8-7-3. When I retrieved the suboptimal solution. I did found certain suboptimal solution including subtour like 3-8-7-3. Please see the code and set node number 33.

    #include "gurobi_c++.h"

    #include <cassert>

    #include <cstdlib>

    #include <cmath>

    #include <sstream>

    #include <vector>

    #include <algorithm>

    using namespace std;

    string itos(int i) {stringstream s; s << i; return s.str(); }

    double distance(double* x, double* y, int i, int j);

    void findsubtour(int n, double** sol, vector<vector<int>> &sub_tour, int* tourlenP);

    // Subtour elimination callback. Whenever a feasible solution is found,

    // find the smallest subtour, and add a subtour elimination constraint

    // if the tour doesn't visit every node.

    class subtourelim: public GRBCallback

    {

    public:

    GRBVar** vars;

    intn;

    subtourelim(GRBVar**xvars,intxn){

    vars=xvars;

    n=xn;

    }

    protected:

    voidcallback(){

    try{

    if(where == GRB_CB_MIPSOL){

    // Found an integer feasible solution - does it visit every node?

    double**x=newdouble*[n];

    for(inti=0;i<n;i++)

    x[i]=getSolution(vars[i],n);

    inttour_len;

    vector<vector<int>>sub_tour;

    findsubtour(n,x,sub_tour,&tour_len);

    for(size_ti=0;i<sub_tour.size();i++){

    if(find(sub_tour[i].begin(),sub_tour[i].end(),0)==sub_tour[i].end()){

    intlen=sub_tour[i].size();

    GRBLinExpr expr = 0;

    for(size_tk=0;k<sub_tour[i].size();k++)

    for(size_tj=k+1;j<sub_tour[i].size();j++)

    expr+=vars[sub_tour[i][k]][sub_tour[i][j]];

    addLazy(expr<=len-1);

    cout << "Adding lazy constraint" << endl;

    }

    }

    for(inti=0;i<n;i++)

    delete[]x[i];

    delete[]x;

    }

    }catch(GRBException e){

    cout << "Error number: " << e.getErrorCode() << endl;

    cout << e.getMessage() << endl;

    }catch(...){

    cout << "Error during callback" << endl;

    }

    }

    };

    // Given an integer-feasible solution 'sol', find the smallest

    // sub-tour. Result is returned in 'tour', and length is

    // returned in 'tourlenP'.

    void

    findsubtour(int n,

    double**sol,

    vector<vector<int>>&sub_tour,

    int*tourlenP)

    {

    bool*seen=newbool[n];

    for(inti=0;i<n;i++)

    seen[i]=false;




    intvisited=0;

    boolflag;

    vector<int>temp_tour;

    do{

    flag=false;

    intnode=0;

    seen[node]=true;

    for(intlen=0;len<n;len++){

    inti;

    for(i=0;i<n;i++){

    if(sol[node][i]>0.5&&!seen[i]){

    if(!node){

    temp_tour.emplace_back(node);

    }

    node=i;

    temp_tour.emplace_back(node);

    seen[node]=true;

    ++visited;

    break;

    }

    }

    if(i==n){

    sub_tour.emplace_back(temp_tour);

    temp_tour.clear();

    break;

    }

    }

    for(intj=1;j<n;j++){

    if(sol[0][j]>0.5&&!seen[j]){

    flag=true;

    break;

    }

    }

    }while(flag);

    cout << "visited = " << visited << endl;

    *tourlenP=visited;




    while(visited<n-1){

    for(inti=1;i<n;i++){

    while(!seen[i]){

    intstart=i;

    for(intlen=0;len<n;len++){

    temp_tour.emplace_back(start);

    ++visited;

    seen[start]=true;

    intj;

    for(j=1;j<n;j++){

    if(sol[start][j]>0.5&&!seen[j]){

    start=j;

    break;

    }

    }

    if(j==n){

    sub_tour.emplace_back(temp_tour);

    temp_tour.clear();

    break;

    }

    }

    }

    }

    }

    // cout << "findsubtour : sub_tour_list" << endl;

    // for (size_t i = 0; i < sub_tour.size(); i++){

    // for (size_t j = 0; j < sub_tour[i].size(); j++){

    // cout << sub_tour[i][j] << " ";

    // }

    // cout << endl;

    // }

    delete[]seen;

    }

    // Euclidean distance between points 'i' and 'j'.

    double

    distance(double* x,

    double*y,

    inti,

    intj)

    {

    doubledx=x[i]-x[j];

    doubledy=y[i]-y[j];

    returnsqrt(dx*dx+dy*dy);

    }

    int

    main(int argc,

    char*argv[])

    {

    if(argc<2){

    cout << "Usage: tsp_c++ size" << endl;

    return1;

    }

    intn=atoi(argv[1]);

    double*x=newdouble[n];

    double*y=newdouble[n];

    inti;

    for(i=0;i<n;i++){

    x[i]=((double)rand())/RAND_MAX;

    y[i]=((double)rand())/RAND_MAX;

    }

    GRBEnv *env = NULL;

    GRBVar **vars = NULL;

    vars = new GRBVar*[n];

    for(i=0;i<n;i++)

    vars[i]=newGRBVar[n];

    try{

    intj;

    env = new GRBEnv();

    GRBModel model = GRBModel(*env);

    // Must set LazyConstraints parameter when using lazy constraints

    model.set(GRB_IntParam_LazyConstraints,1);

    //model.set(GRB_IntParam_PoolSearchMode,1);

    // Create binary decision variables

    for(i=0;i<n;i++){

    for(j=0;j<=i;j++){

    vars[i][j]=model.addVar(0.0,1.0,distance(x,y,i,j),

    GRB_BINARY, "x_"+itos(i)+"_"+itos(j));

    vars[j][i]=vars[i][j];

    }

    }

    // Degree-2 constraints

    GRBLinExpr expr1 = 0;

    for(j=0;j<n;j++){

    expr1+=vars[0][j];

    }

    model.addConstr(expr1>=4,"vehicle_");

    for(i=1;i<n;i++){

    GRBLinExpr expr = 0;

    for(j=0;j<n;j++)

    expr+=vars[i][j];

    model.addConstr(expr==2,"deg2_"+itos(i));

    }

    // Forbid edge from node back to itself

    for(i=0;i<n;i++)

    vars[i][i].set(GRB_DoubleAttr_UB,0);

    // Set callback function

    subtourelimcb=subtourelim(vars,n);

    model.setCallback(&cb);

    // Optimize model

    model.optimize();

    // model.write("tsp_model.lp");

    cout << "########Print the results#########" << endl;

    // Extract solution

    intSolCount=model.get(GRB_IntAttr_SolCount);

    if(SolCount>0){

    for(inti=0;i<SolCount;i++){

    model.set(GRB_IntParam_SolutionNumber,i);

    doublepoolobj=model.get(GRB_DoubleAttr_PoolObjVal);

    cout << "Obj value " << i << ": " << poolobj << " ";

    double**sol=newdouble*[n];

    for(inti=0;i<n;i++)

    sol[i]=model.get(GRB_DoubleAttr_Xn,vars[i],n);

    // for (int i = 0; i < n; i++){

    // for (int j = i+1; j < n; j++) {

    // if (sol[i][j] > 0.5)

    // cout << i << " " << j << " ";

    // }

    // }

    // cout << endl;

    intvisited_len;

    vector<vector<int>>sol_tour;

    findsubtour(n,sol,sol_tour,&visited_len);

    cout << "visited_len = " << visited_len << endl;

    //assert(len == n);

    cout << "sub_tour_list" << endl;

    for(size_ti=0;i<sol_tour.size();i++){

    for(size_tj=0;j<sol_tour[i].size();j++){

    cout << sol_tour[i][j] << " ";

    }

    cout << endl;

    }

    for(inti=0;i<n;i++)

    delete[]sol[i];

    delete[]sol;

    }

    }

    }catch(GRBException e){

    cout << "Error number: " << e.getErrorCode() << endl;

    cout << e.getMessage() << endl;

    }catch(...){

    cout << "Error during optimization" << endl;

    }

    for(i=0;i<n;i++)

    delete[]vars[i];

    delete[] vars;

    delete[]x;

    delete[]y;

    delete env;

    return0;

    }
    And this is one of suboptimal solution I got from solution pool that has sub tour.
    sub_tour_list

    0 17 8 5 6 22

    0 20 15 18 21 3 19 30 24 11 26 9 4 32 12 10 28 13 29 25

    0 23 1 7 31

    2 14 27 16
    0
  • Marika Karbstein
    • Gurobi Staff

    Which Gurobi Version do you use?

    Could you please also add the log file?

    0
  • Dong Han
    • Gurobi-versary
    • Curious
    • Conversationalist

    The Gurobi version I use is 952. When you run this code, you are able to get the log file.

    0
  • Marika Karbstein
    • Gurobi Staff

    There were some fixes for Version 10.0.0, see Recent Bug Fixes by Version - Gurobi Optimization:

    • Fixed issue with not calling the callback for solutions found in MIP cleanup at the end of the solve
    • Fixed a bug that the MIPSOL callback is not called for one of the solutions found

    I think the latter corresponds to what you observed.

    I recommend updating to the newest version.

    0
  • Dong Han
    • Gurobi-versary
    • Curious
    • Conversationalist

    Thank you very much! Problem solved!

    0

投稿コメントは受け付けていません。