I am translating an implementation of a gurobi model to cplex which uses multi-objective functionality and uses the following callbacks. The purpose of the callbacks is to extract the information from the optimization process as the different objectives are optimized.
Gurobi partdef _optimize_callback(model: gb.Model, where: int): if where == GRB.Callback.MULTIOBJ: # change of objective index (multiobjective) model._obj_index = model.cbGet(GRB.Callback.MULTIOBJ_OBJCNT) elif where == GRB.Callback.MIPSOL: obj_best_value = model.cbGet(GRB.Callback.MIPSOL_OBJBST) # Current best objective obj_bound_value = model.cbGet(GRB.Callback.MIPSOL_OBJBND) # Current objective bound #best solution found assign_var_values = model.cbGetSolution(context.assign_vars) solution = [combination for combination in context.assign_vars.keys() if assign_var_values[combination] > 0.99] report_info(model._obj_index, obj_best_value, obj_bound_value, solution) elif where == GRB.Callback.MIP: #update indicators without update sol obj_best_value = model.cbGet(GRB.Callback.MIP_OBJBST) # Current best objective obj_bound_value = model.cbGet(GRB.Callback.MIP_OBJBND) report_info(model._obj_index, obj_best_value, obj_bound_value)I have a test code which uses ProgressListener from docplex. I am new with this module but I think I can already achieve the MIP and MIPSOL callback functionality in gurobi but I have not been able to access the optimization of the different objectives index by index.
from docplex.mp.model import Modelfrom docplex.mp.progress import *import numpy as npclass InfoCallback(SolutionRecorder): def __init__(self, model): super(InfoCallback, self).__init__(ProgressClock.Gap) self.last_obj = None self.best_bound = None self.mip_gap = None self.model = model self.current_obj_index = -1 def notify_start(self): super(InfoCallback, self).notify_start() self.last_obj = None self.current_obj_index = 0 # Start with the first objective def is_improving(self, new_obj, eps=1e-4): last_obj = self.last_obj return last_obj is None or (abs(new_obj- last_obj) >= eps) def notify_progress(self, pdata): super(InfoCallback, self).notify_progress(pdata) if pdata.has_incumbent and self.is_improving(pdata.current_objective): self.last_obj = pdata.current_objective self.mip_gap = pdata.mip_gap self.best_bound = pdata.best_bound print('----> #new objective={0}, bound={1}s, objective={2}, mip={3}'.format(self.last_obj, self.best_bound, self.current_obj_index, self.mip_gap)) else: # a non improving move self.best_bound = pdata.best_bound self.mip_gap = pdata.mip_gap print('----> #new objective={0}, bound={1}s, objective={2}, mip={3}'.format(self.last_obj, self.best_bound, self.current_obj_index, self.mip_gap)) def notify_solution(self, s): super().notify_solution(s) obj_value = s.get_objective_value() # Get the current objective index from the solution print(f"New incumbent solution found with objective value: {obj_value}") multi_obj_values = s.multi_objective_values if multi_obj_values: self.current_obj_index = multi_obj_values.index(max(multi_obj_values)) print(multi_obj_values, self.current_obj_index) #for var in self.model.iter_variables(): # print(f"{var.name}: {s.get_value(var)}") np.random.seed(0)# Example datanum_vehicles = 10num_customers = 40locations = range(num_customers + 1) # Including the depotdistance_matrix = np.random.randint(10, 100, size=(num_customers + 1, num_customers + 1))time_matrix = np.random.randint(200, 600, size=(num_customers + 1, num_customers + 1))gas_matrix = np.random.randint(50, 600, size=(num_customers + 1, num_customers + 1))# Create the modelmodel = Model(name="VRP")# Variablesx = model.binary_var_dict(((i, j, k) for i in locations for j in locations for k in range(num_vehicles) if i != j), name="x")u = model.continuous_var_dict((i for i in locations), name="u") # Subtour elimination variables# Objective: Minimize total distance# Define objectivescost_obj = model.sum(distance_matrix[i][j] * x[i, j, k] for i in locations for j in locations for k in range(num_vehicles) if i != j)time_obj = model.sum(time_matrix[i][j] * x[i, j, k] for i in locations for j in locations for k in range(num_vehicles) if i != j)gas_obj = model.sum(gas_matrix[i][j] * x[i, j, k] for i in locations for j in locations for k in range(num_vehicles) if i != j)# Set multiple objectives with prioritiesmodel.set_multi_objective("min", [cost_obj, time_obj, gas_obj], priorities=[2, 1, 4], names = ['Cost', 'Time', 'Gas'])# Constraints# Each customer is visited exactly oncefor j in locations[1:]: model.add_constraint(model.sum(x[i, j, k] for i in locations for k in range(num_vehicles) if i != j) == 1, f"visit_{j}")# Each vehicle starts from the depot and ends at the depotfor k in range(num_vehicles): model.add_constraint(model.sum(x[0, j, k] for j in locations[1:]) == 1, f"start_{k}") model.add_constraint(model.sum(x[i, 0, k] for i in locations[1:]) == 1, f"end_{k}")# Subtour elimination constraints (MTZ formulation)for i in locations[1:]: for j in locations[1:]: if i != j: for k in range(num_vehicles): model.add_constraint(u[i] - u[j] + num_customers * x[i, j, k] <= num_customers - 1, f"subtour_{i}_{j}_{k}")# Solve the modelsolution_recorder = InfoCallback(model)model.add_progress_listener(solution_recorder)solution = model.solve(clean_before_solve=True)print(f"Objective Value: {solution.objective_value}")print(solution.multi_objective_values)print(solution.solve_details.status)Executing the code I can see the evolution of the optimization process but it only allows me to access the information of one objective. However, at the end, the three optimized objectives appear.
----> #new objective=10892.0, bound=3952.0s, objective=0, mip=0.6371648916636005New incumbent solution found with objective value: 10892.0[10892.0] 0
----> #new objective=2861.0, bound=2861.0s, objective=0, mip=0.0New incumbent solution found with objective value: 2861.0[2861.0] 0
Objective Value: 2861.0[2861.0, 22483.0, 3952.0]multi-objective optimal
How can I access the optimization process objective by objective? I understand that cplex generates a lexicographic order of the objectives by priority and optimizes one by one but my code only gives me information about the objective with the highest priority. Thank you for any help please.