简体   繁体   中英

ChocoSolver - Find the minimal number of path to visit all cities

I am using Choco Solver 4.10.8 in Java for a constraint programming problem. The goal is to find the minimal number of path we need to use to visit all the cities in a matrix M which is defined here.
We have 4 paths, and 6 cities.

static int[][] M = new int[][]{     
        {2, 3, 4, 5, 6},
        {1, 2, 5},
        {2, 3, 4},      
        {3, 4, 6}                     
};

Here the solver should return 0 and 1, which are the indexes of the path we need to take to see all the 6 cities. Indeed, with 2 paths only, we have reached all the cities in the matrix.

I'm a beginner with Choco Solver, and I'm having trouble defining the constraints of the problem. I think that I have well defined the variables of the problem, here are there:

Model model = new Model("minimalPaths");

// VARIABLES

int nbPath = M.length;
int nbCity = 6;

// Table of nbPath length which shows if a path is used (1) or no (0)
IntVar[] usedPath = model.intVarArray("path", nbPath, 0, 1);

// Which paths are used by each city
IntVar[][] city_by_path = model.intVarMatrix("city", nbCity, nbPath,  0, 1);

// Total number of paths used for the solution - we want to minimize that
IntVar totalPath = model.intVar("totalPath", 0, nbPath);        

I chose to use IntVar and not BoolVar because I think that it's easier to deal with the different constraints. Concerning the constraints, I have tried this:

// CONTRAINTES
        
// We loop over the paths of M
for(int i=0; i<nbPath; i++) {
    // We loop over all the cities for a Path
    int[] path = M[i];
    for(int city: path) {
        // If the Path is used or not, we update the value in the table
        model.element(usedPath[i], city_by_path[city-1], model.intVar(i), 0).post();
    }
}

// This constraint is used to have at least one path used for one city
// This means we want to see all the cities
for(int i = 0; i<nbCity; i++) {
    model.sum(city_by_path[i], ">=", model.intVar(1)).post();
}
        
// The sum of all the Path used is the variable we will minimize
// usedPath[i] = 0 or 1 so if we sum all our value, we will have the total number of paths used
model.sum(usedPath, "=", totalPath).post();
    
// We want to minimize the total number of Paths we will use
model.setObjective(Model.MINIMIZE, totalPath);

I'm sure that some of my constraints are badly defined but I really don't know where.
When I try to solve this, I print the object city_by_path associated to the solution. It shows me that the solution is not good and the constraints are not respected...

    path i=2 is used
City 1: 1 0 0 0 
City 2: 0 0 1 0 
City 3: 0 0 1 0 
City 4: 0 0 1 0 
City 5: 0 0 0 1 
City 6: 0 1 0 0 
    Total path used : 1

I was wondering if you can have a look to my code and give me some advices about the definition of my variables and constraints for this problem.

Actually, you don't need to declare the city_by_path variables as new ones. Indeed, either the path is used and its cities are visited, either the path is not used and all cities will be valued to zero in the matrix (which is always true for cities not in the path).

To make it work, you can declare your matrix the following:

IntVar[][] city_by_path = new IntVar[nbCity][nbPath];

And, instead of your element constraints, you fill the matrix with the usedPath variables or with variables valued to zero whether the city is in the path or not:

for (int p = 0; p < nbPath; p++) {
    for (int c = 0; c < nbCity; c++) {
        final int cityId = c + 1;
        if (Arrays.stream(M[p]).anyMatch(i -> i == cityId)) {
            city_by_path[c][p] = usedPath[p];
        } else {
            city_by_path[c][p] = model.intVar(0);
        }
    }
}

The remainder of your code is correct. Also, when solving a CSP with a constraint solver, it is always a good idea to help the solver by specifying on which variables to branch. Branching scheme designed specifically for the problem are generally better to find solutions quickly. Here, you can add the following branching scheme (which an optimal solution in 3 nodes and without any backtrack - especially because the instance of the problem is very easy):

model.getSolver().setSearch(Search.inputOrderLBSearch(usedPath));

Without going into the specifics of Choco, the problem you describe is more commonly known as the Set Covering problem . For some comments on the problem in the context of an assignment in a discrete optimization course see this video .

A simple way to model this is to have a Boolean variable for each of your paths indicating if it is chosen or not. Then for each city, you add a constraint that at least one of the paths that visit that city is chosen. This can be done either with disjunction (an or over the Boolean variables for the paths visiting the current city) or by requiring that the sum of the relevant paths is at least one.

In other words, you should only need a single variable per path. There is no need to find out which path visits a city.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM