Decision Optimization

Decision Optimization

Delivers prescriptive analytics capabilities and decision intelligence to improve decision-making.

 View Only
Expand all | Collapse all

Python: Multi-Objective cplex MILP objectives are rounded

  • 1.  Python: Multi-Objective cplex MILP objectives are rounded

    Posted Tue May 05, 2020 03:30 AM
    Dear all,

    I am experimenting with CPLEX and multi-objective problems, and I found out a bug in the Python interface. Namely, for some reason, when you use the model.set_multi_objective() method the expression you pass for the multi-objective function are "transformed" to discrete ones (even though they're naturally continuous, like dot products with real coefficients) and, consequently, the solution.get_objective_value() returns their value rounded to integers of the real ones.

    The original expressions are unaffected, this means that you can still retrieve the actual real values, however I think that this behaviour deserves investigation (in principle I cannot tell where the rounding takes place and whether it affects the results).

    I attach a MWE to this (which is related to the problem I try to solve):
    import docplex.mp.model as cpx
    from itertools import product
    from collections import defaultdict

    def create_model(papers, authors):
    """
    Create the cplex model for a given optimization problem.

    Parameters
    ----------
    papers : dict
    a dictionary with one entry for each paper.
    The keys are the paper handles and the values are the scores of the paper, one for each author.
    Format example: { '11390/1234567': {'1234': 1.0, '1235': 0.7}, ... }

    authors : dict
    a dictionary with one entry for each author.
    The author id is the key, while his/her status according to
    the different optimization criteria are the values (TODO: still to be considered)
    Format example: { '1234': { 'young': True }, '1235': {'young': False } }

    """
    with cpx.Model(name='VQR Optimization') as model:
    x = defaultdict(lambda: dict()) # x variables are the decision variables from the viewpoint of papers (i.e., they are indexed [paper, author])
    y = defaultdict(lambda: dict()) # y variables are the decision variables from the viewpoint of authors (i.e., they are indexed [author, paper])
    c = defaultdict(lambda: defaultdict(lambda: 0.0)) # c are the coefficients of the paper, indexed as [paper, author]
    d = defaultdict(lambda: defaultdict(lambda: 0.0)) # d are the coefficients of the author, indexed as [author, paper]

    overall_sum_variables = []
    overall_sum_coefficients = []
    young_sum_variables = []
    young_sum_coefficients = []
    for paper, paper_authors in papers.items():
    for author, score in paper_authors.items():
    # score = score * 10
    variable = model.binary_var(f'select[{paper}, {author}]')
    x[paper][author] = variable
    y[author][paper] = variable
    c[paper][author] = score
    d[author][paper] = score
    overall_sum_variables.append(variable)
    overall_sum_coefficients.append(score)
    if authors[author].get('young', False):
    young_sum_variables.append(variable)
    young_sum_coefficients.append(score)

    # each paper selected only once FIXME: just for testing
    model.add_constraint(sum(x[paper].values()) <= 1)

    # each author just one paper FIXME: just for testing
    for author, variables in y.items():
    model.add_constraint(sum(variables.values()) == 1)
    overall = model.dot(overall_sum_variables, overall_sum_coefficients)
    young = model.dot(young_sum_variables, young_sum_coefficients)
    # optimization criterion
    model.set_multi_objective('max', [overall, young], priorities=[2, 1], reltols=[0.1, 0.1], names=['overall', 'young'])

    sol = model.solve(log_output=True)
    if sol is None:
    print('model is infeasible')
    else:
    print(sol)
    print(sol.solve_status)
    print(sol.get_value(overall), sol.get_value(young))
    print(sol.get_objective_value())
    print(model._objective_expr.is_discrete())

    if __name__ == '__main__':
    test_papers = { '11390/1234567': {'1234': 0.7, '1235': 0.7}, '11390/1234568': {'1234': 0.8, '1236': 0.9}, '11390/1234569': {'1235': 0.7, '1236': 0.7},}
    test_authors = { '1234': { 'young': True }, '1235': {}, '1236': { 'young': False } }

    create_model(test_papers, test_authors)

    The output is:
    Version identifier: 12.10.0.0 | 2019-11-26 | 843d4de
    CPXPARAM_Read_DataCheck 1
    CPXPARAM_RandomSeed 201903125

    Multi-objective solve log . . .

    Index Priority Blend Objective Nodes Time (sec.) DetTime (ticks)
    1 2 1 2.3000000000e+00 0 0.00 0.01
    2 1 1 8.0000000000e-01 0 0.00 0.02
    solution for: VQR Optimization
    select[11390/1234567, 1235]=1
    select[11390/1234568, 1234]=1
    select[11390/1234569, 1236]=1

    JobSolveStatus.OPTIMAL_SOLUTION
    2.2 0.8
    [2, 1]
    True


    ------------------------------
    Luca Di Gaspero
    ------------------------------

    #DecisionOptimization


  • 2.  RE: Python: Multi-Objective cplex MILP objectives are rounded

    Posted Tue May 05, 2020 05:04 AM
    Thanks a lot for reporting this. I have reproduced the problem here and filed and appropriate bug report.
    The problem is indeed that all expressions used in the multi-objective are considered discrete. The workaround is what you did in your code: keep a reference to the expressions that make up the individual objectives and then call `get_value()` on them.

    ------------------------------
    Daniel Junglas
    ------------------------------