# --------------------------------------------------------------------------
# File: _multiobj.py
# ---------------------------------------------------------------------------
# Licensed Materials - Property of IBM
# 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5655-Y21
# Copyright IBM Corporation 2008, 2024. All Rights Reserved.
#
# US Government Users Restricted Rights - Use, duplication or
# disclosure restricted by GSA ADP Schedule Contract with
# IBM Corp.
# ------------------------------------------------------------------------
"""Multi-Objective API"""
from ._baseinterface import BaseInterface
from ._subinterfaces import ObjSense
from . import _procedural as _proc
from . import _aux_functions as _aux
from . import _matrices as _mat
from . import _pycplex as CPX
class _Pair():
def __init__(self):
self.first = None
self.second = None
[docs]
class MultiObjInterface(BaseInterface):
"""Methods for adding, querying, and modifying multiple objectives.
The methods in this interface can be used to add, query, and modify
objectives in a specified problem. These objectives are used when
multi-objective optimization is initiated.
See also `MultiObjSolnInterface` where methods for accessing
solutions for multi-objective models can be found.
For more details see the section on multi-objective optimization in
the CPLEX User's Manual.
"""
sense = ObjSense()
"""See `ObjSense()`"""
[docs]
def __init__(self, cpx):
"""Creates a new MultiObjInterface.
The Multi-Objective interface is exposed by the top-level `Cplex`
class as `Cplex.multiobj`. This constructor is not meant to be
used externally.
"""
super().__init__(cplex=cpx, getindexfunc=_proc.multiobjgetindex)
[docs]
def get_num(self):
"""Returns the number of objectives in the problem.
See `CPXgetnumobjs <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/getnumobjs.html>`_ in the Callable Library Reference
Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.multiobj.get_num()
1
>>> indices = c.multiobj.set_num(2)
>>> c.multiobj.get_num()
2
"""
return _proc.getnumobjs(self._env._e, self._cplex._lp)
[docs]
def set_num(self, numobj):
"""Sets the number of objectives in the problem instance.
There is always at least one objective in the problem instance
(indexed 0) thus numobj must be at least 1. If before calling
this method there were more objectives in the instance than the
specified numobj then the objectives whose index is >= numobj are
removed from the instance. If before calling this method the
number of objectives was <= numobj then new objectives are
created, all with all-zero coefficients and default settings
(like priority, weight, etc).
See `CPXsetnumobjs <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/setnumobjs.html>`_ in the Callable Library Reference
Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.multiobj.set_num(2)
>>> c.multiobj.get_num()
2
"""
_proc.setnumobjs(self._env._e, self._cplex._lp, numobj)
[docs]
def get_names(self, *args):
"""Returns the names of a set of objectives.
There are four forms by which multiobj.get_names may be called.
multiobj.get_names()
return the names of all objectives from the problem.
multiobj.get_names(i)
i must be an objective index. Returns the name of row i.
multiobj.get_names(s)
s must be a sequence of objective indices. Returns the names of
the objectives with indices the members of s. Equivalent to
[multiobj.get_names(i) for i in s]
multiobj.get_names(begin, end)
begin and end must be objective indices. Returns the names of
the objectives with indices between begin and end, inclusive of
end. Equivalent to multiobj.get_names(range(begin, end + 1)).
See `CPXmultiobjgetname <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/multiobjgetname.html>`_ in the Callable Library
Reference Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.multiobj.set_definition(0, name='mo1')
>>> c.multiobj.get_names(0)
'mo1'
"""
def _get_name(objidx):
return _proc.multiobjgetname(self._env._e, self._cplex._lp,
objidx)
return _aux.apply_freeform_one_arg(_get_name, self._conv,
self.get_num(), args)
[docs]
def set_name(self, objidx, name):
"""Sets the name of an objective function.
objidx must be an objective name or index.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.multiobj.set_num(3)
>>> for i in range(3):
... c.multiobj.set_name(i, str(i))
>>> c.multiobj.get_names()
['0', '1', '2']
"""
objidx = self._conv(objidx)
_proc.multiobjchgattribs(self._env._e, self._cplex._lp,
objidx, name=name)
[docs]
def get_definition(self, objidx, begin=None, end=None):
"""Returns the definition of an objective.
Returns an objective definitions, where the definition is a list
containing the following components: obj (a list containing the
linear objective coefficients), offset, weight, priority, abstol,
reltol (see `set_definition`).
objidx is the name or index of the objective to be accessed.
The optional begin and end arguments must be variable indices
or names. Together, begin and end specify the range of objective
function coefficients to be returned. By default, the linear
objective coefficients of all variables from the problem will be
returned (i.e., begin will default to the first variable index
and end will default to the last variable index).
See `CPXmultiobjgetobj <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/multiobjgetobj.html>`_ in the Callable Library Reference
Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> varind = list(c.variables.add(obj=[1.0, 2.0]))
>>> c.multiobj.get_definition(0)
[[1.0, 2.0], 0.0, 1.0, 0, 0.0, 0.0]
"""
objidx = self._conv(objidx)
varconv = self._cplex.variables._conv
if begin is None:
begin = 0
else:
begin = varconv(begin)
if end is None:
end = self._cplex.variables.get_num() - 1
else:
end = varconv(end)
return _proc.multiobjgetobj(self._env._e, self._cplex._lp,
objidx, begin, end)
[docs]
def set_definition(self, objidx, obj=None, offset=0.0, weight=1.0,
priority=0, abstol=None, reltol=None, name=None):
"""Sets the definition of an objective.
multiobj.set_definition accepts the keyword arguments objidx,
obj, offset, weight, priority, abstol, reltol, and name.
objidx is the name or index of the objective to be set. The
objective index must be in the interval
[0, Cplex.multiobj.get_num() - 1].
obj can be either a SparsePair or a list of two lists specifying
the linear component of the objective. If not specified, the
coefficients of every variable are set to 0.0.
Note
obj must not contain duplicate indices. If obj references a
variable more than once, either by index, name, or a
combination of index and name, an exception will be raised.
offset is the offset of the objective to be set. If not
specififed, the offset is set to 0.0.
weight is the weight of the objective to be set. For the
definition of the weight see the description of blended objective
in the multi-objective optimization section of the CPLEX User's
Manual. If not specified, the weight is set to 1.0.
priority is the priority of the objective to be set. It must be a
nonnegative integer. For the definition of the priority see the
description of lexicographic objective in the multi-objective
optimization section of the CPLEX User's Manual. If not
specified, the priority is set to 0.
abstol is the absolute tolerance of the objective to be set. If
not specified, the absolute tolerance is set to 0.0.
reltol is the relative tolerance of the objective to be set. If
not specified, the relative tolerance is set to 0.0.
name is a string representing the name of the objective to be
set. If not specified, the objective name will default to None.
See `CPXmultiobjsetobj <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/multiobjsetobj.html>`_ in the Callable Library Reference
Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> varind = list(c.variables.add(names=['x1', 'x2']))
>>> c.multiobj.set_definition(
... objidx=0,
... obj=cplex.SparsePair(ind=varind, val=[1.0, 2.0]),
... offset=0.0,
... weight=1.0,
... priority=0,
... abstol=1e-06,
... reltol=1e-04,
... name='obj1')
>>> c.multiobj.get_definition('obj1')
[[1.0, 2.0], 0.0, 1.0, 0, 1e-06, 0.0001]
>>> c.multiobj.get_names(0)
'obj1'
"""
if obj is None:
obj = _mat.SparsePair()
objind, objval = _mat.unpack_pair(obj)
objind = self._cplex.variables._conv(objind)
if abstol is None:
abstol = self._cplex.parameters.mip.tolerances.absmipgap.default()
if reltol is None:
reltol = self._cplex.parameters.mip.tolerances.mipgap.default()
_proc.multiobjsetobj(self._env._e, self._cplex._lp, objidx, objind,
objval, offset, weight, priority, abstol, reltol,
name)
[docs]
def get_linear(self, objidx, *args):
"""Returns the linear coefficients of a set of variables.
Can be called by four forms each of which requires an objidx
argument. objidx must be an objective name or index.
multiobj.get_linear(objidx)
return the linear objective coefficients of all variables
from the problem.
multiobj.get_linear(objidx, i)
i must be a variable name or index. Returns the linear
objective coefficient of the variable whose index or name is i.
multiobj.get_linear(objidx, s)
s must be a sequence of variable names or indices. Returns the
linear objective coefficient of the variables with indices the
members of s. Equivalent to
[multiobj.get_linear(objidx, i) for i in s]
multiobj.get_linear(objidx, begin, end)
begin and end must be variable indices or variable names.
Returns the linear objective coefficient of the variables with
indices between begin and end, inclusive of end. Equivalent to
multiobj.get_linear(objidx, range(begin, end + 1)).
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... obj=[1.5 * i for i in range(10)],
... names=[str(i) for i in range(10)])
>>> c.variables.get_num()
10
>>> c.multiobj.get_linear(0, 8)
12.0
>>> c.multiobj.get_linear(0, '1', 3)
[1.5, 3.0, 4.5]
>>> c.multiobj.get_linear(0, [2, '0', 5])
[3.0, 0.0, 7.5]
>>> c.multiobj.get_linear(0)
[0.0, 1.5, 3.0, 4.5, 6.0, 7.5, 9.0, 10.5, 12.0, 13.5]
"""
(coeffs, _, _, _, _, _) = self.get_definition(objidx)
def getcoeffs(begin, end=self._cplex.variables.get_num() - 1):
return CPX._getArrayView(coeffs, begin, end + 1)
return _aux.apply_freeform_two_args(
getcoeffs, self._cplex.variables._conv, args)
[docs]
def set_linear(self, objidx, *args):
"""Changes the linear part of an objective function.
Can be called by two forms each of which requires an objidx
argument. objidx must be an objective name or index.
multiobj.set_linear(objidx, var, value)
var must be a variable index or name and value must be a float.
Changes the coefficient of the variable identified by var to
value.
multiobj.set_linear(objidx, sequence)
sequence is a sequence of pairs (var, value) as described
above. Changes the coefficients for the specified variables to
the given values.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=[str(i) for i in range(4)])
>>> c.multiobj.get_linear(0)
[0.0, 0.0, 0.0, 0.0]
>>> c.multiobj.set_linear(0, 0, 1.0)
>>> c.multiobj.get_linear(0)
[1.0, 0.0, 0.0, 0.0]
>>> c.multiobj.set_linear(0, '3', -1.0)
>>> c.multiobj.get_linear(0)
[1.0, 0.0, 0.0, -1.0]
>>> c.multiobj.set_linear(0, [('2', 2.0), (1, 0.5)])
>>> c.multiobj.get_linear(0)
[1.0, 0.5, 2.0, -1.0]
"""
objidx = self._conv(objidx)
pair = _Pair()
# NB: pair.first and pair.second get set as a side effect of
# running apply_pairs below!
def set_pair(first, second):
pair.first = first
pair.second = second
_aux.apply_pairs(set_pair, self._cplex.variables._conv, *args)
ncols = self._cplex.variables.get_num()
allind = list(range(ncols))
# To preserve the values that have not been provided, we query
# current objective. This is to maintain consistent semantics
# with Cplex.objective.set_linear.
allval = self._cplex.multiobj.get_linear(objidx)
for idx, val in zip(pair.first, pair.second):
allval[idx] = val
_proc.multiobjsetobj(self._env._e, self._cplex._lp, objidx,
objind=allind, objval=allval)
[docs]
def get_sense(self):
"""Returns the sense of all objective functions.
See `ObjSense`.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.multiobj.sense[c.multiobj.get_sense()]
'minimize'
>>> c.multiobj.set_sense(c.multiobj.sense.maximize)
>>> c.multiobj.sense[c.multiobj.get_sense()]
'maximize'
>>> c.multiobj.set_sense(c.multiobj.sense.minimize)
>>> c.multiobj.sense[c.multiobj.get_sense()]
'minimize'
"""
return self._cplex.objective.get_sense()
[docs]
def set_sense(self, sense):
"""Sets the sense of all objective functions.
Note
All objective functions share the same sense. To model an
objective with a different sense use a negative value for the
weight attribute. See `set_weight`.
The argument to this method must be either `ObjSense.minimize`
or `ObjSense.maximize`.
>>> import cplex
>>> c = cplex.Cplex()
>>> c.multiobj.sense[c.multiobj.get_sense()]
'minimize'
>>> c.multiobj.set_sense(c.multiobj.sense.maximize)
>>> c.multiobj.sense[c.multiobj.get_sense()]
'maximize'
>>> c.multiobj.set_sense(c.multiobj.sense.minimize)
>>> c.multiobj.sense[c.multiobj.get_sense()]
'minimize'
"""
self._cplex.objective.set_sense(sense)
[docs]
def get_offset(self, objidx):
"""Returns the constant offset of an objective function.
objidx must be an objective name or index.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(obj=[1.0 for i in range(3)])
>>> c.multiobj.get_offset(0)
0.0
"""
(_, offset, _, _, _, _) = self.get_definition(objidx)
return offset
[docs]
def set_offset(self, objidx, offset):
"""Sets the constant offset of an objective function.
objidx must be an objective name or index.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(obj=[1.0 for i in range(3)])
>>> c.objective.set_offset(3.14)
>>> c.objective.get_offset()
3.14
"""
objidx = self._conv(objidx)
_proc.multiobjchgattribs(self._env._e, self._cplex._lp,
objidx, offset=offset)
[docs]
def get_weight(self, objidx):
"""Returns the weight of an objective function.
objidx must be an objective name or index.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(obj=[1.0 for i in range(3)])
>>> c.multiobj.get_weight(0)
1.0
"""
(_, _, weight, _, _, _) = self.get_definition(objidx)
return weight
[docs]
def set_weight(self, objidx, weight):
"""Sets the weight of an objective function.
objidx must be an objective name or index.
Note
All objective functions share the same sense. To model an
objective with a different sense use a negative value for the
weight attribute. See `set_sense`.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(obj=[1.0 for i in range(3)])
>>> c.multiobj.set_weight(0, -2.0)
>>> c.multiobj.get_weight(0)
-2.0
"""
objidx = self._conv(objidx)
_proc.multiobjchgattribs(self._env._e, self._cplex._lp,
objidx, weight=weight)
[docs]
def get_priority(self, objidx):
"""Returns the priority of an objective function.
objidx must be an objective name or index.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(obj=[1.0 for i in range(3)])
>>> c.multiobj.get_priority(0)
0
"""
(_, _, _, priority, _, _) = self.get_definition(objidx)
return priority
[docs]
def set_priority(self, objidx, priority):
"""Sets the priority of an objective function.
objidx must be an objective name or index.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(obj=[1.0 for i in range(3)])
>>> c.multiobj.set_priority(0, 2)
>>> c.multiobj.get_priority(0)
2
"""
objidx = self._conv(objidx)
_proc.multiobjchgattribs(self._env._e, self._cplex._lp,
objidx, priority=priority)
[docs]
def get_abstol(self, objidx):
"""Returns the absolute tolerance of an objective function.
objidx must be an objective name or index.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(obj=[1.0 for i in range(3)])
>>> c.multiobj.get_abstol(0)
0.0
"""
(_, _, _, _, abstol, _) = self.get_definition(objidx)
return abstol
[docs]
def set_abstol(self, objidx, abstol):
"""Sets the absolute tolerance of an objective function.
objidx must be an objective name or index.
abstol should be a float. When specifying a new value, the same
limits apply as with the
Cplex.parameters.mip.tolerances.absmipgap parameter. See the
section on Specifying multiple objective problems in the CPLEX
User's Manual for the details on the meaning of this tolerance.
See `CPXmultiobjchgattribs <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/multiobjchgattribs.html>`_ in the Callable Library
Reference Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(obj=[1.0 for i in range(3)])
>>> c.multiobj.set_abstol(0, 1e-6)
>>> c.multiobj.get_abstol(0)
1e-06
"""
objidx = self._conv(objidx)
_proc.multiobjchgattribs(self._env._e, self._cplex._lp,
objidx, abstol=abstol)
[docs]
def get_reltol(self, objidx):
"""Returns the relative tolerance of an objective function.
objidx must be an objective name or index.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(obj=[1.0 for i in range(3)])
>>> c.multiobj.get_reltol(0)
0.0
"""
(_, _, _, _, _, reltol) = self.get_definition(objidx)
return reltol
[docs]
def set_reltol(self, objidx, reltol):
"""Sets the relative tolerance of an objective function.
objidx must be an objective name or index.
reltol should be a float. When specifying a new value, the same
limits apply as with the Cplex.parameters.mip.tolerances.mipgap
parameter. Note that a nondefault setting of this parameter only
applies to MIP multiobjective problems. See the section on
Specifying multiple objective problems in the CPLEX User's Manual
for the details on the meaning of this tolerance.
See `CPXmultiobjchgattribs <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/multiobjchgattribs.html>`_ in the Callable Library
Reference Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(obj=[1.0 for i in range(3)])
>>> c.multiobj.set_reltol(0, 1e-4)
>>> c.multiobj.get_reltol(0)
0.0001
"""
objidx = self._conv(objidx)
_proc.multiobjchgattribs(self._env._e, self._cplex._lp,
objidx, reltol=reltol)