1

I am working on defining a diet model, to extract all possible solutions of diets with both environmental and nutritional constraints. I have used the same setup as in this docplex-example at GitHub to do the optimization and included environmental constraints as well: diet.pyhttps://github.com/IBMDecisionOptimization/docplex-examples/blob/master/examples/mp/modeling/diet.py

Then, to obtain solutions from the pool of (non optimal) feasible solutions, I have added this part as well:

 def soln_pool(mdl):
     cpx = mdl.get_cplex()
     cpx.parameters.mip.pool.intensity.set(4)
     cpx.parameters.mip.limits.populate.set(1000000)
         
     try:
         cpx.populate_solution_pool()
     except CplexSolverError:
        print("Exception raised during populate")
        return []          
     numsol = cpx.solution.pool.get_num()
     print("The solution pool contains %d solutions." % numsol)
     meanobjval = cpx.solution.pool.get_mean_objective_value()
     print("The average objective value of the solutions is %.10g." % meanobjval)
     
     nb_vars = mdl.number_of_variables
     
     sol_pool = []

     for i in range(numsol):
        x_i = cpx.solution.pool.get_values(i)
        assert len(x_i) == nb_vars
        
        sol = []
        for k in range(nb_vars):
            sol.append(x_i[k])
        sol_pool.append(sol) 
     return sol_pool

results = soln_pool(mdl)
label=data.index
matrix_results=pd.DataFrame()
    
for s, sol in enumerate(results,start =1):
    matrix_results[str(s)]=sol
    matrix_results.index=data.index

I am interested to obtain the span/range of "all solutions" to see which diets can fulfill my criteria. Therefore I set Pool Intensity = 4 (aggressive) and the Populate limits = high number, to get as many solutions as possible. However, I sometimes get a very high number of solutions, and using 1000000 as the limit does not even retrieve all solutions. Since I am not interested in all solutions as such, but rather the "span" of solutions, I would like to achieve a pool of solutions, including both the best(optimised) and the worst solutions, while still seeing some steps in between. So, is it possible to skip some solutions with a step function, so that instead of getting all solutions, I get all solutions being at least x (e.g. 5) different from each other, and thereby reducing be pool?

Furthermore, it seems like, even though I put a high number, I do not always retrieve all solutions, e.g. I get 3000 solutions even though I know others exist. Does this mean that setting the population limit and intensity will not ensure that I get all solutions?

Finally, any other inputs/ideas to how I can setup such a model, will be highly appreciated!

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
  • The set of all diets may be difficult to describe. For some mathy background, you can look [here](https://en.wikipedia.org/wiki/Convex_polytope): V-representation (each point is a combination of diets) vs. H-representation (each element is a vector of (protein, carb, fat)). Often, V-representation requires a lot more space than H-representation. You could compute upper and lower bounds on each of the input variables if you wanted to, but most solutions might end up unfeasible given your original constraints. – hilberts_drinking_problem Jan 03 '22 at 11:47

1 Answers1

1

You could use the solution pool replacement strategy

Designates the strategy for replacing a solution in the solution pool when the solution pool has reached its capacity.

The docplex python parameter is parameters.mip.pool.replace

And with value 2

Replace solutions in order to build a set of diverse solutions

You could also enumerate without solution pools:

mdl = Model(name='buses')
nbbus40 = mdl.integer_var(name='nbBus40')
nbbus30 = mdl.integer_var(name='nbBus30')
mdl.add_constraint(nbbus40*40 + nbbus30*30 >= 300, 'kids')
mdl.minimize(nbbus40**2*500 + nbbus30**2*400)

nb_iter=5

for iter in range(0,nb_iter):
    mdl.solve()
    nbbus40sol=int(nbbus40.solution_value)
    nbbus30sol=int(nbbus30.solution_value)
    print(int(nbbus40sol)," buses 40 seats")
    print(int(nbbus30sol)," buses 30 seats")
    print("cost : ",mdl.objective_value)
    print()
    mdl.add_constraint(mdl.logical_or((nbbus40sol!=nbbus40),
            nbbus30sol!=nbbus30))
Alex Fleischer
  • 9,276
  • 2
  • 12
  • 15
  • Thank you for your answer Alex! For solution 1, should this be added as a parameter both in the above and in the model? For solution 2, which seems interesting, would you say it is also working for complex examples? For instance, I have more than 1000 food items with respectively 10 nutritional factors and 5 environmental factors, and each food item needs to fulfill both nutritional and environmental bounds. And the nb_iter, does that correspond with the Populate Limit in cplex? – Caroline Gebara Jan 04 '22 at 07:47
  • Hi, sol 1 cpx.parameters.mip.pool.replace.set(2) . Sol 2 can also work with big model and nb_iter is the number of solutions you will get – Alex Fleischer Jan 04 '22 at 08:11