Forum Discussion

FICO's avatar
FICO
Community Manager
1 year ago

How to apply termination conditions to each objective function in multi-objective model

Hello everyone,

I am implementing a multi-objective model in Python, and I would like to set a time limit for each objective function. It would be best if I could set different time limits for each, but if that is not possible, it is okay to set the same time limit for each objective function.

I have looked through some documentation, but I was unable to find the answer I was looking for. 

I tried setting several time-related parameters such as ‘timelimit’, but it seems that these only limit the total execution time.

self.model.setObjectiveN(stock_out_min_urgent,   0, 24, 1.0, 0.0, 0.0, 'StockOutMinUrgent')

self.model.setObjectiveN(stock_out_min_OEM,     2, 22, 1.0, 0.0, 0.0, 'StockOutMinOEM')

self.model.setObjectiveN(stock_out_min_KD,     4, 20, 1.0, 0.0, 0.0, 'StockOutMinKD')

self.model.setObjectiveN(stock_out_min_AS,     6, 18, 1.0, 0.0, 0.0, 'StockOutMinAS')

self.model.setObjectiveN(fixed_plan_change_min,   8, 16, 1.0, 0.0, 0.0, 'FixedPlanChangeMin')

self.model.setObjectiveN(init_mold_prod_max,    9, 15, 1.0, 0.0, 0.0, 'Init_mold_prod_max')

self.model.setObjectiveN(inven_prod_max,      10, 14, 1.0, 0.0, 0.0,'InvenProdPriorityMax')       

self.model.setObjectiveN(mold_use_min,       12, 12, 1.0, 0.0, 0.0,'MoldUseMin')     

self.model.setObjectiveN(prod_priority_max,     14, 8, 1.0, 0.0, 0.0, 'ProdPriorityMax')

self.model.setObjectiveN(worker_priority_max,    16, 6, 1.0, 0.0, 0.0, 'WorkerPriorityMax')

self.model.setObjectiveN(worker_min,        18, 4, 1.0, 0.0, 0.0, 'WorkerMin')

self.model.setObjectiveN(workerY_min2,       20, 3, 1.0, 0.0, 0.0, 'WorkerYMin2')

self.model.setObjectiveN(yprod_min,         22, 2, 1.0, 0.0, 0.0, 'ItemProdMin')

self.model.setObjectiveN(item_use_min,       24, 1, 1.0, 0.0, 0.0, 'ItemUseMin')  

self.model.controls.timelimit = 30

I might just not be able to find the answer, but I'd really appreciate your help.

Best regards,

Kiseop

4 Replies

  • FICO's avatar
    FICO
    Community Manager

    Hi,

    If the time limit is hit for an objective, do you want to stop the optimization altogether, or continue the solve with the next objective?

    Regards,

    Chris

  • FICO's avatar
    FICO
    Community Manager

    Dear Chris,

    I want to continue to solve.

    Regards,

    Kiseop

  • FICO's avatar
    FICO
    Community Manager

    We don't have a good way to do this at the moment. Something you can try is to implement your own soft time limit in a callback. If there is a callback which is frequently called while solving your problem, you can use it to check whether your time limit has been hit, and if so, set mipabsstop to a large value to make the Optimizer accept the current solution. Then reset mipabsstop at the start of the next objective (in the beforeobjective callback).

    Unfortunately there is not a convenient callback in which to check time limits. I would suggest trying the cutround callback, added in Xpress 9.6, which is called at every opportunity for cut separation during the solve, which means once every round of cuts added at the root node, and at least once per node solve in the branch and bound tree. This might provide enough resolution to implement your time limits.

    Below is an example showing roughly how to do this. In a future release we plan to add proper support for this use case.

    Regards,

    Chris

    import xpress as xp
     
     
    time_limits = [
      10, 20, 30, 40, 50   # etc.
    ]
     
     
    def cutround(cb_prob, data, ifxpress):
      """Called at every opportunity for cut separation during the solve,
      which means once every round of cuts added at the root node, and at
      least once per node solve in the branch and bound tree."""
      # Check the time limit for this objective
      global time_limits
      current_obj_index = cb_prob.attributes.solvedobjs
      time_limit = time_limits[current_obj_index]
      # The time attribute is reset to zero at the start of every objective
      solve_time = cb_prob.attributes.time
      if (
        (solve_time > time_limit) and
        # Only move on to the next objective if we found a solution (optional)
        (cb_prob.attributes.mipsols > 0)
      ):
        # Accept the current solution
        print(f'Objective {current_obj_index + 1} hit time limit after {solve_time} seconds')
        cb_prob.controls.mipabsstop = xp.infinity
     
     
    def beforeobjective(cb_prob, data):
      """Called before every objective is solved"""
      # Reset stopping conditions
      cb_prob.controls.mipabsstop = 0
     
     
    model = xp.problem()
     
    # Define problem here...
     
    model.addcbcutround(cutround)
    model.addcbbeforeobjective(beforeobjective)
     
    model.optimize()
  • FICO's avatar
    FICO
    Community Manager

    Dear Chris,

    I tried implementing it based on the code you provided, and it seems to be working fine for now.

    Thank you for your help.

    In future releases, it would be nice to have a feature that allows you to set different termination conditions for each objective in a multi-objective model.

    In Gurobi, this feature can be easily applied as follows.

    self.model.setObjectiveN(stock_out_min_urgent,   0, 15, 1.0, 0.0, 0.0, ‘StockOutMinUrgent’)

    self.model.setObjectiveN(stock_out_min_OEM,     1, 14, 1.0, 0.0, 0.0, ‘StockOutMinOEM’)

    env0 = self.model.getMultiobjEnv(0)

    env1 = self.model.getMultiobjEnv(1)

    env0.setParam(‘TimeLimit’, 30)

    env0.setParam(‘MIPGap’, 0.05)

    env1.setParam(‘TimeLimit’, 30)

    Thank you.

    Best regards,

    Kiseop