Quantcast
Channel: Active questions tagged python - Stack Overflow
Viewing all articles
Browse latest Browse all 23390

Use ProgressListener with multiobjective in docplex (CPLEX)

$
0
0

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.


Viewing all articles
Browse latest Browse all 23390

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>