# --------------------------------------------------------------------------
# File: _subinterfaces.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.
# ------------------------------------------------------------------------
"""Sub-interfaces of the CPLEX API."""
from contextlib import closing, contextmanager
from io import StringIO
import numbers
from . import _constants
from . import _procedural as CPX_PROC
from ._baseinterface import BaseInterface
from ._multiobjsoln import MultiObjSolnInterface
from ._matrices import (SparsePair, SparseTriple, _HBMatrix,
unpack_pair, unpack_triple)
from ._aux_functions import (apply_freeform_one_arg,
apply_freeform_two_args,
max_arg_length,
validate_arg_lengths, apply_pairs,
delete_set_by_range,
make_group, _group,
init_list_args, listify,
unzip, convert_sequence)
from ..exceptions import CplexSolverError, WrongNumberOfArgumentsError
from ..constant_class import ConstantClass
[docs]
class Histogram():
"""A class to retrieve histogram data of the columns or rows of the
linear constraint matrix.
See `VariablesInterface.get_histogram()` and
`LinearConstraintInterface.get_histogram()`.
"""
[docs]
def __init__(self, c, key):
self.__hist = CPX_PROC.gethist(c._env._e, c._lp, key[0])
self.orientation = key
[docs]
def __getitem__(self, key):
"""Returns the number of columns/rows with a given nonzero count."""
if isinstance(key, int):
if key < 0:
raise IndexError("histogram keys must be non-negative")
return self.__hist[key]
if isinstance(key, slice):
start, stop, step = key.start, key.stop, key.step
if start is None:
start = 0
if stop is None or stop > len(self.__hist):
stop = len(self.__hist)
if step is None:
step = 1
if start < 0:
raise IndexError("histogram keys must be non-negative")
if stop < 0:
raise IndexError("histogram keys must be non-negative")
return [self.__hist[i] for i in range(start, stop, step)]
raise TypeError("key must be an integer or a slice")
[docs]
def __str__(self):
"""Returns a string containing a histogram in human readable form."""
if self.orientation[0] == "c":
hdr0 = "Column counts (excluding fixed variables):"
hdr1 = " Nonzero Count:"
hdr2 = "Number of Columns:"
else:
hdr0 = "Row counts (excluding fixed variables):"
hdr1 = " Nonzero Count:"
hdr2 = "Number of Rows:"
rng = len(self.__hist)
maxhist = max(self.__hist)
length = max(2,
len(str(rng)),
len(str(maxhist))) + 2
perline = max((75 - len(hdr1)) / length, 1)
ret = ""
i = 0
needs_hdr0 = True
while True:
if i >= rng:
break
for j in range(i, rng):
if self.__hist[j] != 0:
break
else:
break
if needs_hdr0:
ret = ret + hdr0 + "\n\n"
needs_hdr0 = False
ret = ret + hdr1
k = 0
for j in range(i, rng):
if k >= perline:
break
if self.__hist[j] == 0:
continue
ret = ret + str("%*d" % (length, j))
k += 1
ret = ret + "\n"
ret = ret + hdr2
k = 0
jj = i
for j in range(i, rng):
if k >= perline:
break
jj += 1
if self.__hist[j] == 0:
continue
ret = ret + str("%*d" % (length, self.__hist[j]))
k += 1
ret = ret + "\n\n"
i = jj
return ret
[docs]
class AdvancedVariablesInterface(BaseInterface):
"""Methods for advanced operations on variables."""
[docs]
def __init__(self, parent):
"""Creates a new AdvancedVariablesInterface.
The advanced variables interface is exposed by the top-level
`Cplex` class as Cplex.variables.advanced. This constructor is
not meant to be used externally.
"""
super().__init__(cplex=parent._cplex, advanced=True)
[docs]
def protect(self, *args):
"""Prevents variables from being aggregated during presolve.
protect may be called with either a single variable identifier
or a sequence of variable identifiers. A variable identifier
is either an index or a name of a variable.
Note
Subsequent calls to protect will replace previously protected
variables with the new set of protected variables.
Note
If presolve can fix a variable to a value, it will be removed
from the problem even if it has been protected.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["a", "b", "c", "d"])
>>> c.variables.advanced.protect("a")
>>> c.variables.advanced.protect(["b", "d"])
"""
a = listify(self._cplex.variables._conv(args[0]))
CPX_PROC.copyprotected(self._env._e, self._cplex._lp, a)
[docs]
def get_protected(self):
"""Returns the currently protected variables.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["a", "b", "c", "d"])
>>> c.variables.advanced.protect("a")
>>> c.variables.advanced.get_protected()
[0]
>>> c.variables.advanced.protect(["b", "d"])
>>> c.variables.advanced.get_protected()
[1, 3]
"""
return CPX_PROC.getprotected(self._env._e, self._cplex._lp)
[docs]
def tighten_lower_bounds(self, *args):
"""Tightens the lower bounds on the specified variables.
There are two forms by which
variables.advanced.tighten_lower_bounds may be called.
variables.advanced.tighten_lower_bounds(i, lb)
i must be a variable name or index and lb must be a real
number. Sets the lower bound of the variable whose index
or name is i to lb.
variables.advanced.tighten_lower_bounds(seq_of_pairs)
seq_of_pairs must be a list or tuple of (i, lb) pairs, each
of which consists of a variable name or index and a real
number. Sets the lower bound of the specified variables to
the corresponding values. Equivalent to
[variables.advanced.tighten_lower_bounds(pair[0], pair[1]) for pair in seq_of_pairs].
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["x0", "x1", "x2"])
>>> c.variables.advanced.tighten_lower_bounds(0, 1.0)
>>> c.variables.get_lower_bounds()
[1.0, 0.0, 0.0]
>>> c.variables.advanced.tighten_lower_bounds([(2, 3.0), ("x1", -1.0)])
>>> c.variables.get_lower_bounds()
[1.0, -1.0, 3.0]
"""
def tlb(a, b):
CPX_PROC.tightenbds(self._env._e, self._cplex._lp, a,
'L' * len(a), b)
apply_pairs(tlb, self._cplex.variables._conv, *args)
[docs]
def tighten_upper_bounds(self, *args):
"""Tightens the upper bounds on the specified variables.
There are two forms by which
variables.advanced.tighten_upper_bounds may be called.
variables.advanced.tighten_upper_bounds(i, lb)
i must be a variable name or index and lb must be a real
number. Sets the upper bound of the variable whose index
or name is i to lb.
variables.advanced.tighten_upper_bounds(seq_of_pairs)
seq_of_pairs must be a list or tuple of (i, lb) pairs, each
of which consists of a variable name or index and a real
number. Sets the upper bound of the specified variables to
the corresponding values. Equivalent to
[variables.advanced.tighten_upper_bounds(pair[0], pair[1]) for pair in seq_of_pairs].
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["x0", "x1", "x2"])
>>> c.variables.advanced.tighten_upper_bounds(0, 1.0)
>>> c.variables.advanced.tighten_upper_bounds([(2, 3.0), ("x1", 10.0)])
>>> c.variables.get_upper_bounds()
[1.0, 10.0, 3.0]
"""
def tub(a, b):
CPX_PROC.tightenbds(self._env._e, self._cplex._lp, a,
'U' * len(a), b)
apply_pairs(tub, self._cplex.variables._conv, *args)
[docs]
class VarTypes(ConstantClass):
"""Constants defining variable types
For a definition of each type, see those topics in the CPLEX User's
Manual.
"""
continuous = _constants.CPX_CONTINUOUS
binary = _constants.CPX_BINARY
integer = _constants.CPX_INTEGER
semi_integer = _constants.CPX_SEMIINT
semi_continuous = _constants.CPX_SEMICONT
[docs]
class VariablesInterface(BaseInterface):
"""Methods for adding, querying, and modifying variables.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["x0", "x1", "x2"])
>>> # default values for lower_bounds are 0.0
>>> c.variables.get_lower_bounds()
[0.0, 0.0, 0.0]
>>> # values can be set either one at a time or many at a time
>>> c.variables.set_lower_bounds(0, 1.0)
>>> c.variables.set_lower_bounds([("x1", -1.0), (2, 3.0)])
>>> # values can be queried as a range
>>> c.variables.get_lower_bounds(0, "x1")
[1.0, -1.0]
>>> # values can be queried as a sequence in arbitrary order
>>> c.variables.get_lower_bounds(["x1", "x2", 0])
[-1.0, 3.0, 1.0]
>>> # can query the number of variables
>>> c.variables.get_num()
3
>>> c.variables.set_types(0, c.variables.type.binary)
>>> c.variables.get_num_binary()
1
"""
type = VarTypes()
"""See `VarTypes()` """
[docs]
def __init__(self, cplex):
"""Creates a new VariablesInterface.
The variables interface is exposed by the top-level `Cplex` class
as `Cplex.variables`. This constructor is not meant to be used
externally.
"""
super().__init__(cplex=cplex, getindexfunc=CPX_PROC.getcolindex)
self.advanced = AdvancedVariablesInterface(self)
"""See `AdvancedVariablesInterface()` """
[docs]
def get_num(self):
"""Returns the number of variables in the problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> t = c.variables.type
>>> indices = c.variables.add(types = [t.continuous, t.binary, t.integer])
>>> c.variables.get_num()
3
"""
return CPX_PROC.getnumcols(self._env._e, self._cplex._lp)
[docs]
def get_num_integer(self):
"""Returns the number of integer variables in the problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> t = c.variables.type
>>> indices = c.variables.add(types = [t.continuous, t.binary, t.integer])
>>> c.variables.get_num_integer()
1
"""
return CPX_PROC.getnumint(self._env._e, self._cplex._lp)
[docs]
def get_num_binary(self):
"""Returns the number of binary variables in the problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> t = c.variables.type
>>> indices = c.variables.add(types = [t.semi_continuous, t.binary, t.integer])
>>> c.variables.get_num_binary()
1
"""
return CPX_PROC.getnumbin(self._env._e, self._cplex._lp)
[docs]
def get_num_semicontinuous(self):
"""Returns the number of semi-continuous variables in the problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> t = c.variables.type
>>> indices = c.variables.add(types = [t.semi_continuous, t.semi_integer, t.semi_integer])
>>> c.variables.get_num_semicontinuous()
1
"""
return CPX_PROC.getnumsemicont(self._env._e, self._cplex._lp)
[docs]
def get_num_semiinteger(self):
"""Returns the number of semi-integer variables in the problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> t = c.variables.type
>>> indices = c.variables.add(types = [t.semi_continuous, t.semi_integer, t.semi_integer])
>>> c.variables.get_num_semiinteger()
2
"""
return CPX_PROC.getnumsemiint(self._env._e, self._cplex._lp)
def _add(self, obj, lb, ub, types, names, columns):
"""non-public"""
if not isinstance(types, str):
types = "".join(types)
arg_list = [obj, lb, ub, types, names, columns]
num_new_cols = max_arg_length(arg_list)
validate_arg_lengths(
arg_list,
extra_msg=": obj, lb, ub, types, names, columns"
)
num_old_cols = self.get_num()
if columns == []:
CPX_PROC.newcols(self._env._e, self._cplex._lp, obj, lb, ub,
types, names)
else:
with CPX_PROC.chbmatrix(columns, self._cplex._env_lp_ptr,
1) as (cmat, nnz):
CPX_PROC.addcols(self._env._e, self._cplex._lp,
num_new_cols, nnz, obj,
cmat, lb, ub, names)
if types != "":
CPX_PROC.chgctype(
self._env._e, self._cplex._lp,
list(range(num_old_cols, num_old_cols + num_new_cols)),
types)
[docs]
def add(self, obj=None, lb=None, ub=None, types="", names=None,
columns=None):
"""Adds variables and related data to the problem.
variables.add accepts the keyword arguments obj, lb, ub,
types, names, and columns.
If more than one argument is specified, all arguments must
have the same length.
obj is a list of floats specifying the linear objective
coefficients of the variables.
lb is a list of floats specifying the lower bounds on the
variables.
ub is a list of floats specifying the upper bounds on the
variables.
types must be either a list of single-character strings or a
string containing the types of the variables.
Note
If types is specified, the problem type will be a MIP, even if
all variables are specified to be continuous.
names is a list of strings.
columns may be either a list of sparse vectors or a matrix in
list-of-lists format.
Note
The entries of columns must not contain duplicate indices.
If an entry of columns references a row more than once,
either by index, name, or a combination of index and name,
an exception will be raised.
Returns an iterator containing the indices of the added
variables.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(names=["c0", "c1", "c2"])
>>> indices = c.variables.add(
... obj=[1.0, 2.0, 3.0],
... types=[c.variables.type.integer] * 3)
>>> indices = c.variables.add(
... obj=[1.0, 2.0, 3.0],
... lb=[-1.0, 1.0, 0.0],
... ub=[100.0, cplex.infinity, cplex.infinity],
... types=[c.variables.type.integer] * 3,
... names=["0", "1", "2"],
... columns=[cplex.SparsePair(ind=['c0', 2],
... val=[1.0, -1.0]),
... [['c2'],[2.0]],
... cplex.SparsePair(ind=[0, 1],
... val=[3.0, 4.0])])
>>> c.variables.get_lower_bounds()
[0.0, 0.0, 0.0, -1.0, 1.0, 0.0]
>>> c.variables.get_cols("1")
SparsePair(ind = [2], val = [2.0])
"""
obj, lb, ub, names, columns = init_list_args(obj, lb, ub, names,
columns)
return self._add_iter(self.get_num, self._add,
obj, lb, ub, types, names, columns)
[docs]
def delete(self, *args):
"""Deletes variables from the problem.
There are four forms by which variables.delete may be called.
variables.delete()
deletes all variables from the problem.
variables.delete(i)
i must be a variable name or index. Deletes the variable
whose index or name is i.
variables.delete(s)
s must be a sequence of variable names or indices. Deletes
the variables with names or indices contained within s.
Equivalent to [variables.delete(i) for i in s].
variables.delete(begin, end)
begin and end must be variable indices or variable names.
Deletes the variables with indices between begin and end,
inclusive of end. Equivalent to
variables.delete(range(begin, end + 1)). This will give the
best performance when deleting batches of variables.
See `CPXdelcols <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/delcols.html>`_ in the Callable Library Reference Manual
for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=[str(i) for i in range(10)])
>>> c.variables.get_num()
10
>>> c.variables.delete(8)
>>> c.variables.get_names()
['0', '1', '2', '3', '4', '5', '6', '7', '9']
>>> c.variables.delete("1", 3)
>>> c.variables.get_names()
['0', '4', '5', '6', '7', '9']
>>> c.variables.delete([2, "0", 5])
>>> c.variables.get_names()
['4', '6', '7']
>>> c.variables.delete()
>>> c.variables.get_names()
[]
"""
def _delete(begin, end=None):
CPX_PROC.delcols(self._env._e, self._cplex._lp, begin, end)
delete_set_by_range(_delete, self._conv, self.get_num(), *args)
[docs]
def set_lower_bounds(self, *args):
"""Sets the lower bound for a variable or set of variables.
There are two forms by which variables.set_lower_bounds may be
called.
variables.set_lower_bounds(i, lb)
i must be a variable name or index and lb must be a real
number. Sets the lower bound of the variable whose index
or name is i to lb.
variables.set_lower_bounds(seq_of_pairs)
seq_of_pairs must be a list or tuple of (i, lb) pairs, each
of which consists of a variable name or index and a real
number. Sets the lower bound of the specified variables to
the corresponding values. Equivalent to
[variables.set_lower_bounds(pair[0], pair[1]) for pair in seq_of_pairs].
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["x0", "x1", "x2"])
>>> c.variables.set_lower_bounds(0, 1.0)
>>> c.variables.get_lower_bounds()
[1.0, 0.0, 0.0]
>>> c.variables.set_lower_bounds([(2, 3.0), ("x1", -1.0)])
>>> c.variables.get_lower_bounds()
[1.0, -1.0, 3.0]
"""
def setlb(a, b):
CPX_PROC.chgbds(self._env._e, self._cplex._lp, a, "L" * len(a), b)
apply_pairs(setlb, self._conv, *args)
[docs]
def set_upper_bounds(self, *args):
"""Sets the upper bound for a variable or set of variables.
There are two forms by which variables.set_upper_bounds may be
called.
variables.set_upper_bounds(i, ub)
i must be a variable name or index and ub must be a real
number. Sets the upper bound of the variable whose index
or name is i to ub.
variables.set_upper_bounds(seq_of_pairs)
seq_of_pairs must be a list or tuple of (i, ub) pairs, each
of which consists of a variable name or index and a real
number. Sets the upper bound of the specified variables to
the corresponding values. Equivalent to
[variables.set_upper_bounds(pair[0], pair[1]) for pair in seq_of_pairs].
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["x0", "x1", "x2"])
>>> c.variables.set_upper_bounds(0, 1.0)
>>> c.variables.set_upper_bounds([("x1", 10.0), (2, 3.0)])
>>> c.variables.get_upper_bounds()
[1.0, 10.0, 3.0]
"""
def setub(a, b):
CPX_PROC.chgbds(self._env._e, self._cplex._lp, a, "U" * len(a), b)
apply_pairs(setub, self._conv, *args)
[docs]
def set_names(self, *args):
"""Sets the name of a variable or set of variables.
There are two forms by which variables.set_names may be
called.
variables.set_names(i, name)
i must be a variable name or index and name must be a
string.
variables.set_names(seq_of_pairs)
seq_of_pairs must be a list or tuple of (i, name) pairs,
each of which consists of a variable name or index and a
string. Sets the name of the specified variables to the
corresponding strings. Equivalent to
[variables.set_names(pair[0], pair[1]) for pair in seq_of_pairs].
>>> import cplex
>>> c = cplex.Cplex()
>>> t = c.variables.type
>>> indices = c.variables.add(types = [t.continuous, t.binary, t.integer])
>>> c.variables.set_names(0, "first")
>>> c.variables.set_names([(2, "third"), (1, "second")])
>>> c.variables.get_names()
['first', 'second', 'third']
"""
def setnames(a, b):
CPX_PROC.chgcolname(self._env._e, self._cplex._lp, a, b)
apply_pairs(setnames, self._conv, *args)
[docs]
def set_types(self, *args):
"""Sets the type of a variable or set of variables.
There are two forms by which variables.set_types may be
called.
variables.set_types(i, type)
i must be a variable name or index and name must be a
single-character string.
variables.set_types(seq_of_pairs)
seq_of_pairs must be a list or tuple of (i, type) pairs,
each of which consists of a variable name or index and a
single-character string. Sets the type of the specified
variables to the corresponding strings. Equivalent to
[variables.set_types(pair[0], pair[1]) for pair in seq_of_pairs].
Note
If the types are set, the problem will be treated as a MIP,
even if all variable types are continuous.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(5)])
>>> c.variables.set_types(0, c.variables.type.continuous)
>>> c.variables.set_types([("1", c.variables.type.integer),
("2", c.variables.type.binary),
("3", c.variables.type.semi_continuous),
("4", c.variables.type.semi_integer)])
>>> c.variables.get_types()
['C', 'I', 'B', 'S', 'N']
>>> c.variables.type[c.variables.get_types(0)]
'continuous'
"""
if len(args) == 2:
indices = [self._conv(args[0])]
xctypes = args[1]
elif len(args) == 1:
indices, xctypes = unzip(args[0])
indices = self._conv(indices)
xctypes = "".join(xctypes)
else:
raise WrongNumberOfArgumentsError()
CPX_PROC.chgctype(self._env._e, self._cplex._lp, indices, xctypes)
[docs]
def get_lower_bounds(self, *args):
"""Returns the lower bounds on variables from the problem.
There are four forms by which variables.get_lower_bounds may be called.
variables.get_lower_bounds()
return the lower bounds on all variables from the problem.
variables.get_lower_bounds(i)
i must be a variable name or index. Returns the lower
bound on the variable whose index or name is i.
variables.get_lower_bounds(s)
s must be a sequence of variable names or indices. Returns
the lower bounds on the variables with indices the members
of s. Equivalent to
[variables.get_lower_bounds(i) for i in s]
variables.get_lower_bounds(begin, end)
begin and end must be variable indices or variable names.
Returns the lower bounds on the variables with indices between
begin and end, inclusive of end. Equivalent to
variables.get_lower_bounds(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(lb = [1.5 * i for i in range(10)],
names = [str(i) for i in range(10)])
>>> c.variables.get_num()
10
>>> c.variables.get_lower_bounds(8)
12.0
>>> c.variables.get_lower_bounds("1",3)
[1.5, 3.0, 4.5]
>>> c.variables.get_lower_bounds([2,"0",5])
[3.0, 0.0, 7.5]
>>> c.variables.get_lower_bounds()
[0.0, 1.5, 3.0, 4.5, 6.0, 7.5, 9.0, 10.5, 12.0, 13.5]
"""
def getlb(a, b=self.get_num() - 1):
return CPX_PROC.getlb(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(getlb, self._conv, args)
[docs]
def get_upper_bounds(self, *args):
"""Returns the upper bounds on variables from the problem.
There are four forms by which variables.get_upper_bounds may be called.
variables.get_upper_bounds()
return the upper bounds on all variables from the problem.
variables.get_upper_bounds(i)
i must be a variable name or index. Returns the upper
bound on the variable whose index or name is i.
variables.get_upper_bounds(s)
s must be a sequence of variable names or indices. Returns
the upper bounds on the variables with indices the members
of s. Equivalent to
[variables.get_upper_bounds(i) for i in s]
variables.get_upper_bounds(begin, end)
begin and end must be variable indices or variable names.
Returns the upper bounds on the variables with indices between
begin and end, inclusive of end. Equivalent to
variables.get_upper_bounds(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(ub = [(1.5 * i) + 1.0 for i in range(10)],
names = [str(i) for i in range(10)])
>>> c.variables.get_num()
10
>>> c.variables.get_upper_bounds(8)
13.0
>>> c.variables.get_upper_bounds("1",3)
[2.5, 4.0, 5.5]
>>> c.variables.get_upper_bounds([2,"0",5])
[4.0, 1.0, 8.5]
>>> c.variables.get_upper_bounds()
[1.0, 2.5, 4.0, 5.5, 7.0, 8.5, 10.0, 11.5, 13.0, 14.5]
"""
def getub(a, b=self.get_num() - 1):
return CPX_PROC.getub(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(getub, self._conv, args)
[docs]
def get_names(self, *args):
"""Returns the names of variables from the problem.
There are four forms by which variables.get_names may be called.
variables.get_names()
return the names of all variables from the problem.
variables.get_names(i)
i must be a variable index. Returns the name of variable i.
variables.get_names(s)
s must be a sequence of variable indices. Returns the
names of the variables with indices the members of s.
Equivalent to [variables.get_names(i) for i in s]
variables.get_names(begin, end)
begin and end must be variable indices. Returns the names of
the variables with indices between begin and end, inclusive of
end. Equivalent to variables.get_names(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ['x' + str(i) for i in range(10)])
>>> c.variables.get_num()
10
>>> c.variables.get_names(8)
'x8'
>>> c.variables.get_names(1,3)
['x1', 'x2', 'x3']
>>> c.variables.get_names([2,0,5])
['x2', 'x0', 'x5']
>>> c.variables.get_names()
['x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9']
"""
def getname(a, b=self.get_num() - 1):
return CPX_PROC.getcolname(
self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(getname, self._conv, args)
[docs]
def get_types(self, *args):
"""Returns the types of variables from the problem.
There are four forms by which variables.types may be called.
variables.types()
return the types of all variables from the problem.
variables.types(i)
i must be a variable name or index. Returns the type of
the variable whose index or name is i.
variables.types(s)
s must be a sequence of variable names or indices. Returns
the types of the variables with indices the members of s.
Equivalent to [variables.get_types(i) for i in s]
variables.types(begin, end)
begin and end must be variable indices or variable names.
Returns the types of the variables with indices between begin
and end, inclusive of end. Equivalent to
variables.get_types(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> t = c.variables.type
>>> indices = c.variables.add(names = [str(i) for i in range(5)],
types = [t.continuous, t.integer,
t.binary, t.semi_continuous, t.semi_integer])
>>> c.variables.get_num()
5
>>> c.variables.get_types(3)
'S'
>>> c.variables.get_types(1,3)
['I', 'B', 'S']
>>> c.variables.get_types([2,0,4])
['B', 'C', 'N']
>>> c.variables.get_types()
['C', 'I', 'B', 'S', 'N']
"""
def gettype(a, b=self.get_num() - 1):
return CPX_PROC.getctype(self._env._e, self._cplex._lp, a, b)
t = [i for i in "".join(apply_freeform_two_args(
gettype, self._conv, args))]
return t[0] if len(t) == 1 else t
[docs]
def get_cols(self, *args):
"""Returns a set of columns of the linear constraint matrix.
Returns a list of SparsePair instances or a single SparsePair
instance, depending on the form by which it was called.
There are four forms by which variables.get_cols may be called.
variables.get_cols()
return the entire linear constraint matrix.
variables.get_cols(i)
i must be a variable name or index. Returns the column of
the linear constraint matrix associated with variable i.
variables.get_cols(s)
s must be a sequence of variable names or indices. Returns
the columns of the linear constraint matrix associated with
the variables with indices the members of s. Equivalent to
[variables.get_cols(i) for i in s]
variables.get_cols(begin, end)
begin and end must be variable indices or variable names.
Returns the columns of the linear constraint matrix associated
with the variables with indices between begin and end,
inclusive of end. Equivalent to
variables.get_cols(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(names=['c1', 'c2'])
>>> indices = c.variables.add(
... names=[str(i) for i in range(3)],
... columns=[cplex.SparsePair(ind=['c1'], val=[1.0]),
... cplex.SparsePair(ind=['c2'], val=[2.0]),
... cplex.SparsePair(ind=['c1','c2'], val=[3.0, 4.0])])
>>> c.variables.get_num()
3
>>> c.variables.get_cols(2)
SparsePair(ind = [0, 1], val = [3.0, 4.0])
>>> for col in c.variables.get_cols(1, 2):
... print(col)
SparsePair(ind = [1], val = [2.0])
SparsePair(ind = [0, 1], val = [3.0, 4.0])
>>> for col in c.variables.get_cols([2, 0, 1]):
... print(col)
SparsePair(ind = [0, 1], val = [3.0, 4.0])
SparsePair(ind = [0], val = [1.0])
SparsePair(ind = [1], val = [2.0])
>>> for col in c.variables.get_cols():
... print(col)
SparsePair(ind = [0], val = [1.0])
SparsePair(ind = [1], val = [2.0])
SparsePair(ind = [0, 1], val = [3.0, 4.0])
"""
def getcols(a, b=self.get_num() - 1):
mat = _HBMatrix()
t = CPX_PROC.getcols(self._env._e, self._cplex._lp, a, b)
mat.matbeg = t[0]
mat.matind = t[1]
mat.matval = t[2]
return [m for m in mat]
return apply_freeform_two_args(getcols, self._conv, args)
[docs]
def get_histogram(self):
"""Returns a histogram of the columns of the linear constraint matrix.
To access the number of columns with given nonzero counts, use
slice notation. If a negative nonzero count is queried in
this manner an IndexError will be raised.
The __str__ method of the `Histogram` object returns a string
displaying the number of columns with given nonzeros counts in
human readable form.
The data member "orientation" of the histogram object is
"column", indicating that the histogram shows the nonzero
counts for the columns of the linear constraint matrix.
>>> import cplex
>>> c = cplex.Cplex("ind.lp")
>>> histogram = c.variables.get_histogram()
>>> print(histogram)
Column counts (excluding fixed variables):
<BLANKLINE>
Nonzero Count: 1 2 3
Number of Columns: 1 6 36
<BLANKLINE>
<BLANKLINE>
>>> histogram[2]
6
>>> histogram[0:4]
[0, 1, 6, 36]
"""
return Histogram(self._cplex, "column")
[docs]
class AdvancedLinearConstraintInterface(BaseInterface):
"""Methods for handling lazy cuts and user cuts.
Lazy cuts are constraints not specified in the constraint
matrix of the MIP problem, but that must be not be violated
in a solution. Using lazy cuts makes sense when there are a
large number of constraints that must be satisfied at a solution,
but are unlikely to be violated if they are left out. When
you add lazy cuts to your model, set the CPLEX parameter
c.parameters.preprocessing.reduce to 0 (zero) or 1 (one)
in order to turn off dual reductions.
User cuts are constraints that are implied by the constraint
matrix and integrality requirements. Adding user cuts is helpful
to tighten the MIP formulation. When you add user cuts, set
the CPLEX parameter cplex.parameters.preprocessing.reformulations to
cplex.parameters.preprocessing.reformulations.values.interfere_uncrush or
cplex.parameters.preprocessing.reformulations.values.none to make sure
that CPLEX makes only reductions that do not interfere with crushing
linear forms (cuts in this case).
"""
[docs]
def __init__(self, parent):
"""Creates a new AdvancedLinearConstraintInterface.
The advanced linear constraints interface is exposed by the
top-level `Cplex` class as Cplex.linear_constraints.advanced.
This constructor is not meant to be used externally.
"""
super().__init__(cplex=parent._cplex, advanced=True)
[docs]
def get_num_lazy_constraints(self):
"""Returns the number of lazy cuts in the problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=[str(i) for i in range(10)])
>>> cut = cplex.SparsePair(ind=[0, 1, 4], val=[1.0, 1.0, 1.0])
>>> indices = c.linear_constraints.advanced.add_lazy_constraints(
... lin_expr=[cut],
... senses="E",
... rhs=[0.0],
... names=["lz1"])
>>> c.linear_constraints.advanced.get_num_lazy_constraints()
1
"""
return CPX_PROC._getnumlazyconstraints(self._env._e, self._cplex._lp)
[docs]
def get_num_user_cuts(self):
"""Returns the number of user cuts in the problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=[str(i) for i in range(10)])
>>> cut = cplex.SparsePair(ind=[0, 1, 4], val=[1.0, 1.0, 1.0])
>>> indices = c.linear_constraints.advanced.add_user_cuts(
... lin_expr=[cut],
... senses="E",
... rhs=[0.0],
... names=["usr1"])
>>> c.linear_constraints.advanced.get_num_user_cuts()
1
"""
return CPX_PROC._getnumusercuts(self._env._e, self._cplex._lp)
def _add_lazy_constraints(self, lin_expr, senses, rhs, names):
"""non-public"""
if not isinstance(senses, str):
senses = "".join(senses)
validate_arg_lengths(
[rhs, senses, names, lin_expr],
extra_msg=": lin_expr, senses, rhs, names"
)
CPX_PROC.addlazyconstraints(
self._env._e, self._cplex._lp, rhs, senses,
lin_expr, names)
[docs]
def add_lazy_constraints(self, lin_expr=None, senses="", rhs=None,
names=None):
"""Adds lazy constraints to the problem.
linear_constraints.advanced.add_lazy_constraints accepts the
keyword arguments lin_expr, senses, rhs, and names.
If more than one argument is specified, all arguments must
have the same length.
lin_expr may be either a list of SparsePair instances or a
matrix in list-of-lists format.
Note
The entries of lin_expr must not contain duplicate indices.
If an entry of lin_expr references a variable more than
once, either by index, name, or a combination of index and
name, an exception will be raised.
senses must be either a list of single-character strings or a
string containing the senses of the linear constraints.
rhs is a list of floats, specifying the righthand side of
each linear constraint.
names is a list of strings.
Returns an iterator containing the indices of the added lazy
constraints.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=[str(i) for i in range(10)])
>>> cut = cplex.SparsePair(ind=[0, 1, 4], val=[1.0, 1.0, 1.0])
>>> indices = c.linear_constraints.advanced.add_lazy_constraints(
... lin_expr=[cut],
... senses="E",
... rhs=[0.0],
... names=["lz1"])
>>> cut2 = cplex.SparsePair(ind=[0, 2, 4], val=[1.0, 1.0, 1.0])
>>> cut3 = cplex.SparsePair(ind=[0, 2, 5], val=[1.0, 1.0, 1.0])
>>> indices = c.linear_constraints.advanced.add_lazy_constraints(
... lin_expr=[cut2, cut3],
... senses="EE",
... rhs=[0.0, 0.0],
... names=["lz2", "lz3"])
>>> c.linear_constraints.advanced.get_num_lazy_constraints()
3
"""
lin_expr, rhs, names = init_list_args(lin_expr, rhs, names)
return self._add_iter(self.get_num_lazy_constraints,
self._add_lazy_constraints,
lin_expr, senses, rhs, names)
def _add_user_cuts(self, lin_expr, senses, rhs, names):
"""non-public"""
if not isinstance(senses, str):
senses = "".join(senses)
validate_arg_lengths(
[rhs, senses, names, lin_expr],
extra_msg=": lin_expr, senses, rhs, names"
)
CPX_PROC.addusercuts(
self._env._e, self._cplex._lp, rhs, senses,
lin_expr, names)
[docs]
def add_user_cuts(self, lin_expr=None, senses="", rhs=None, names=None):
"""Adds user cuts to the problem.
linear_constraints.advanced.add_user_cuts accepts the keyword
arguments lin_expr, senses, rhs, and names.
If more than one argument is specified, all arguments must
have the same length.
lin_expr may be either a list of SparsePair instances or a
matrix in list-of-lists format.
Note
The entries of lin_expr must not contain duplicate indices.
If an entry of lin_expr references a variable more than
once, either by index, name, or a combination of index and
name, an exception will be raised.
senses must be either a list of single-character strings or a
string containing the senses of the linear constraints.
rhs is a list of floats, specifying the righthand side of
each linear constraint.
names is a list of strings.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=[str(i) for i in range(10)])
>>> cut = cplex.SparsePair(ind=[0, 1, 4], val=[1.0, 1.0, 1.0])
>>> indices = c.linear_constraints.advanced.add_user_cuts(
... names=["usr1"],
... lin_expr=[cut],
... senses="E",
... rhs=[0.0])
>>> cut2 = cplex.SparsePair(ind=[0, 2, 4], val=[1.0, 1.0, 1.0])
>>> cut3 = cplex.SparsePair(ind=[0, 2, 5], val=[1.0, 1.0, 1.0])
>>> indices = c.linear_constraints.advanced.add_user_cuts(
... lin_expr=[cut2, cut3],
... senses = "EE",
... rhs=[0.0, 0.0],
... names=["usr2", "usr3"])
>>> c.linear_constraints.advanced.get_num_user_cuts()
3
"""
lin_expr, senses, rhs, names = init_list_args(
lin_expr, senses, rhs, names)
return self._add_iter(self.get_num_user_cuts, self._add_user_cuts,
lin_expr, senses, rhs, names)
[docs]
def free_lazy_constraints(self):
"""Removes all lazy constraints from the problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=[str(i) for i in range(10)])
>>> cut = cplex.SparsePair(ind=[0, 1, 4], val=[1.0, 1.0, 1.0])
>>> indices = c.linear_constraints.advanced.add_lazy_constraints(
... lin_expr = [cut],
... senses = "E",
... rhs = [0.0],
... names = ["lz1"])
>>> c.linear_constraints.advanced.get_num_lazy_constraints()
1
>>> c.linear_constraints.advanced.free_lazy_constraints()
>>> c.linear_constraints.advanced.get_num_lazy_constraints()
0
"""
CPX_PROC.freelazyconstraints(self._env._e, self._cplex._lp)
[docs]
def free_user_cuts(self):
"""Removes all user cuts from the problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=[str(i) for i in range(10)])
>>> cut = cplex.SparsePair(ind=[0, 1, 4], val=[1.0, 1.0, 1.0])
>>> indices = c.linear_constraints.advanced.add_user_cuts(
... lin_expr=[cut],
... senses="E",
... rhs=[0.0],
... names=["usr1"])
>>> c.linear_constraints.advanced.get_num_user_cuts()
1
>>> c.linear_constraints.advanced.free_user_cuts()
>>> c.linear_constraints.advanced.get_num_user_cuts()
0
"""
CPX_PROC.freeusercuts(self._env._e, self._cplex._lp)
[docs]
class LinearConstraintInterface(BaseInterface):
"""Methods for adding, modifying, and querying linear constraints."""
[docs]
def __init__(self, cplex):
"""Creates a new LinearConstraintInterface.
The linear constraints interface is exposed by the top-level
`Cplex` class as `Cplex.linear_constraints`. This constructor is
not meant to be used externally.
"""
super().__init__(cplex=cplex, getindexfunc=CPX_PROC.getrowindex)
self.advanced = AdvancedLinearConstraintInterface(self)
"""See `AdvancedLinearConstraintInterface()` """
[docs]
def get_num(self):
"""Returns the number of linear constraints.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(names = ["c1", "c2", "c3"])
>>> c.linear_constraints.get_num()
3
"""
return CPX_PROC.getnumrows(self._env._e, self._cplex._lp)
def _add(self, lin_expr, senses, rhs, range_values, names):
"""non-public"""
if not isinstance(senses, str):
senses = "".join(senses)
arg_list = [rhs, senses, range_values, names, lin_expr]
num_new_rows = max_arg_length(arg_list)
validate_arg_lengths(
arg_list,
extra_msg=": lin_expr, senses, rhs, range_values, names"
)
num_old_rows = self.get_num()
if lin_expr:
with CPX_PROC.chbmatrix(lin_expr, self._cplex._env_lp_ptr,
0) as (rmat, nnz):
CPX_PROC.addrows(self._env._e, self._cplex._lp, 0,
num_new_rows, nnz, rhs, senses,
rmat, [], names)
if range_values != []:
CPX_PROC.chgrngval(
self._env._e, self._cplex._lp,
list(range(num_old_rows, num_old_rows + num_new_rows)),
range_values)
else:
if senses.find('R') != -1 and not range_values:
range_values = [0.0] * len(senses)
CPX_PROC.newrows(self._env._e, self._cplex._lp, rhs, senses,
range_values, names)
[docs]
def add(self, lin_expr=None, senses="", rhs=None, range_values=None,
names=None):
"""Adds linear constraints to the problem.
linear_constraints.add accepts the keyword arguments lin_expr,
senses, rhs, range_values, and names.
If more than one argument is specified, all arguments must
have the same length.
lin_expr may be either a list of SparsePair instances or a
matrix in list-of-lists format.
Note
The entries of lin_expr must not contain duplicate indices.
If an entry of lin_expr references a variable more than
once, either by index, name, or a combination of index and
name, an exception will be raised.
senses must be either a list of single-character strings or a
string containing the senses of the linear constraints.
Each entry must
be one of 'G', 'L', 'E', and 'R', indicating
greater-than-or-equal-to (>=), less-than-or-equal-to (<=),
equality (=), and ranged constraints, respectively.
rhs is a list of floats, specifying the righthand side of
each linear constraint.
range_values is a list of floats, specifying the difference
between lefthand side and righthand side of each linear constraint.
If range_values[i] > 0 (zero) then the constraint i is defined as
rhs[i] <= rhs[i] + range_values[i]. If range_values[i] < 0 (zero)
then constraint i is defined as
rhs[i] + range_value[i] <= a*x <= rhs[i].
names is a list of strings.
Returns an iterator containing the indices of the added linear
constraints.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["x1", "x2", "x3"])
>>> indices = c.linear_constraints.add(
lin_expr = [cplex.SparsePair(ind = ["x1", "x3"], val = [1.0, -1.0]),
cplex.SparsePair(ind = ["x1", "x2"], val = [1.0, 1.0]),
cplex.SparsePair(ind = ["x1", "x2", "x3"], val = [-1.0] * 3),
cplex.SparsePair(ind = ["x2", "x3"], val = [10.0, -2.0])],
senses = ["E", "L", "G", "R"],
rhs = [0.0, 1.0, -1.0, 2.0],
range_values = [0.0, 0.0, 0.0, -10.0],
names = ["c0", "c1", "c2", "c3"])
>>> c.linear_constraints.get_rhs()
[0.0, 1.0, -1.0, 2.0]
"""
lin_expr, senses, rhs, range_values, names = init_list_args(
lin_expr, senses, rhs, range_values, names)
return self._add_iter(self.get_num, self._add,
lin_expr, senses, rhs, range_values, names)
[docs]
def delete(self, *args):
"""Removes linear constraints from the problem.
There are four forms by which linear_constraints.delete may be
called.
linear_constraints.delete()
deletes all linear constraints from the problem.
linear_constraints.delete(i)
i must be a linear constraint name or index. Deletes the
linear constraint whose index or name is i.
linear_constraints.delete(s)
s must be a sequence of linear constraint names or indices.
Deletes the linear constraints with names or indices contained
within s. Equivalent to [linear_constraints.delete(i) for i in s].
linear_constraints.delete(begin, end)
begin and end must be linear constraint indices or linear
constraint names. Deletes the linear constraints with indices
between begin and end, inclusive of end. Equivalent to
linear_constraints.delete(range(begin, end + 1)). This will
give the best performance when deleting batches of linear
constraints.
See `CPXdelrows <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/delrows.html>`_ in the Callable Library Reference Manual
for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(names=[str(i) for i in range(10)])
>>> c.linear_constraints.get_num()
10
>>> c.linear_constraints.delete(8)
>>> c.linear_constraints.get_names()
['0', '1', '2', '3', '4', '5', '6', '7', '9']
>>> c.linear_constraints.delete("1", 3)
>>> c.linear_constraints.get_names()
['0', '4', '5', '6', '7', '9']
>>> c.linear_constraints.delete([2, "0", 5])
>>> c.linear_constraints.get_names()
['4', '6', '7']
>>> c.linear_constraints.delete()
>>> c.linear_constraints.get_names()
[]
"""
def _delete(begin, end=None):
CPX_PROC.delrows(self._env._e, self._cplex._lp, begin, end)
delete_set_by_range(_delete, self._conv, self.get_num(), *args)
[docs]
def set_rhs(self, *args):
"""Sets the righthand side of a set of linear constraints.
There are two forms by which linear_constraints.set_rhs may be
called.
linear_constraints.set_rhs(i, rhs)
i must be a row name or index and rhs must be a real number.
Sets the righthand side of the row whose index or name is
i to rhs.
linear_constraints.set_rhs(seq_of_pairs)
seq_of_pairs must be a list or tuple of (i, rhs) pairs, each
of which consists of a row name or index and a real
number. Sets the righthand side of the specified rows to
the corresponding values. Equivalent to
[linear_constraints.set_rhs(pair[0], pair[1]) for pair in seq_of_pairs].
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(names = ["c0", "c1", "c2", "c3"])
>>> c.linear_constraints.get_rhs()
[0.0, 0.0, 0.0, 0.0]
>>> c.linear_constraints.set_rhs("c1", 1.0)
>>> c.linear_constraints.get_rhs()
[0.0, 1.0, 0.0, 0.0]
>>> c.linear_constraints.set_rhs([("c3", 2.0), (2, -1.0)])
>>> c.linear_constraints.get_rhs()
[0.0, 1.0, -1.0, 2.0]
"""
def chgrhs(a, b):
CPX_PROC.chgrhs(self._env._e, self._cplex._lp, a, b)
apply_pairs(chgrhs, self._conv, *args)
[docs]
def set_names(self, *args):
"""Sets the name of a linear constraint or set of linear constraints.
There are two forms by which linear_constraints.set_names may be
called.
linear_constraints.set_names(i, name)
i must be a linear constraint name or index and name must be a
string.
linear_constraints.set_names(seq_of_pairs)
seq_of_pairs must be a list or tuple of (i, name) pairs,
each of which consists of a linear constraint name or index and a
string. Sets the name of the specified linear constraints to the
corresponding strings. Equivalent to
[linear_constraints.set_names(pair[0], pair[1]) for pair in seq_of_pairs].
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(names = ["c0", "c1", "c2", "c3"])
>>> c.linear_constraints.set_names("c1", "second")
>>> c.linear_constraints.get_names(1)
'second'
>>> c.linear_constraints.set_names([("c3", "last"), (2, "middle")])
>>> c.linear_constraints.get_names()
['c0', 'second', 'middle', 'last']
"""
def setnames(a, b):
CPX_PROC.chgrowname(self._env._e, self._cplex._lp, a, b)
apply_pairs(setnames, self._conv, *args)
[docs]
def set_senses(self, *args):
"""Sets the sense of a linear constraint or set of linear constraints.
There are two forms by which linear_constraints.set_senses may be
called.
linear_constraints.set_senses(i, type)
i must be a row name or index and name must be a
single-character string.
linear_constraints.set_senses(seq_of_pairs)
seq_of_pairs must be a list or tuple of (i, sense) pairs,
each of which consists of a row name or index and a
single-character string. Sets the sense of the specified
rows to the corresponding strings. Equivalent to
[linear_constraints.set_senses(pair[0], pair[1]) for pair in seq_of_pairs].
The senses of the constraints must be one of 'G', 'L', 'E',
and 'R', indicating greater-than-or-equal-to (>=),
less-than-or-equal-to (<=), equality (=), and ranged constraints,
respectively.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(names = ["c0", "c1", "c2", "c3"])
>>> c.linear_constraints.get_senses()
['E', 'E', 'E', 'E']
>>> c.linear_constraints.set_senses("c1", "G")
>>> c.linear_constraints.get_senses(1)
'G'
>>> c.linear_constraints.set_senses([("c3", "L"), (2, "R")])
>>> c.linear_constraints.get_senses()
['E', 'G', 'R', 'L']
"""
if len(args) == 2:
indices = [self._conv(args[0])]
senses = args[1]
elif len(args) == 1:
indices, senses = unzip(args[0])
indices = self._conv(indices)
senses = "".join(senses)
else:
raise WrongNumberOfArgumentsError()
CPX_PROC.chgsense(self._env._e, self._cplex._lp,
indices, senses)
[docs]
def set_linear_components(self, *args):
"""Sets a linear constraint or set of linear constraints.
There are two forms by which this method may be called:
linear_constraints.set_linear_components(i, lin)
i must be a row name or index and lin must be either a
SparsePair or a pair of sequences, the first of which
consists of variable names or indices, the second of which
consists of floats.
linear_constraints.set_linear_components(seq_of_pairs)
seq_of_pairs must be a list or tuple of (i, lin) pairs,
each of which consists of a row name or index and a vector
as described above. Sets the specified rows
to the corresponding vector. Equivalent to
[linear_constraints.set_linear_components(pair[0], pair[1])
for pair in seq_of_pairs].
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(names=["c0", "c1", "c2", "c3"])
>>> indices = c.variables.add(names=["x0", "x1"])
>>> c.linear_constraints.set_linear_components("c0", [["x0"], [1.0]])
>>> c.linear_constraints.get_rows("c0")
SparsePair(ind = [0], val = [1.0])
>>> c.linear_constraints.set_linear_components([
... ("c3", cplex.SparsePair(ind=["x1"], val=[-1.0])),
... (2, [[0, 1], [-2.0, 3.0]])])
>>> c.linear_constraints.get_rows("c3")
SparsePair(ind = [1], val = [-1.0])
>>> c.linear_constraints.get_rows(2)
SparsePair(ind = [0, 1], val = [-2.0, 3.0])
"""
def setlin(cons, vars_):
validate_arg_lengths([cons, vars_])
cons = convert_sequence(cons, self._get_index)
varcache = {}
for c, v in zip(cons, vars_):
ind, val = unpack_pair(v)
CPX_PROC.chgcoeflist(
self._env._e,
self._cplex._lp,
[c] * len(ind),
convert_sequence(
ind,
self._cplex.variables._get_index,
varcache
),
val
)
apply_pairs(setlin, self._conv, *args)
[docs]
def set_range_values(self, *args):
"""Sets the range values for a set of linear constraints.
That is, this method sets the lefthand side (lhs) for each ranged
constraint of the form lhs <= lin_expr <= rhs.
The range values are a list of floats, specifying the difference
between lefthand side and righthand side of each linear constraint.
If range_values[i] > 0 (zero) then the constraint i is defined as
rhs[i] <= rhs[i] + range_values[i]. If range_values[i] < 0 (zero)
then constraint i is defined as
rhs[i] + range_value[i] <= a*x <= rhs[i].
Note that changing the range values will not change the sense of a
constraint; you must call the method set_senses() of the class
LinearConstraintInterface to change the sense of a ranged row if
the previous range value was 0 (zero) and the constraint sense was not
'R'. Similarly, changing the range coefficient from a nonzero value to
0 (zero) will not change the constraint sense from 'R" to "E"; an
additional call of setsenses() is required to accomplish that.
There are two forms by which linear_constraints.set_range_values may be
called.
linear_constraints.set_range_values(i, range)
i must be a row name or index and range must be a real
number. Sets the range value of the row whose index or
name is i to range.
linear_constraints.set_range_values(seq_of_pairs)
seq_of_pairs must be a list or tuple of (i, range) pairs, each
of which consists of a row name or index and a real
number. Sets the range values for the specified rows to
the corresponding values. Equivalent to
[linear_constraints.set_range_values(pair[0], pair[1]) for pair in seq_of_pairs].
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(names = ["c0", "c1", "c2", "c3"])
>>> c.linear_constraints.set_range_values("c1", 1.0)
>>> c.linear_constraints.get_range_values()
[0.0, 1.0, 0.0, 0.0]
>>> c.linear_constraints.set_range_values([("c3", 2.0), (2, -1.0)])
>>> c.linear_constraints.get_range_values()
[0.0, 1.0, -1.0, 2.0]
"""
def chgrngval(a, b):
CPX_PROC.chgrngval(self._env._e, self._cplex._lp, a, b)
apply_pairs(chgrngval, self._conv, *args)
[docs]
def set_coefficients(self, *args):
"""Sets individual coefficients of the linear constraint matrix.
There are two forms by which
linear_constraints.set_coefficients may be called.
linear_constraints.set_coefficients(row, col, val)
row and col must be indices or names of a linear constraint
and variable, respectively. The corresponding coefficient
is set to val.
linear_constraints.set_coefficients(coefficients)
coefficients must be a list of (row, col, val) triples as
described above.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(names = ["c0", "c1", "c2", "c3"])
>>> indices = c.variables.add(names = ["x0", "x1"])
>>> c.linear_constraints.set_coefficients("c0", "x1", 1.0)
>>> c.linear_constraints.get_rows(0)
SparsePair(ind = [1], val = [1.0])
>>> c.linear_constraints.set_coefficients([("c2", "x0", 2.0),
("c2", "x1", -1.0)])
>>> c.linear_constraints.get_rows("c2")
SparsePair(ind = [0, 1], val = [2.0, -1.0])
"""
if len(args) == 3:
arg_list = [[arg] for arg in args]
elif len(args) == 1:
arg_list = unzip(args[0])
else:
raise WrongNumberOfArgumentsError()
CPX_PROC.chgcoeflist(
self._env._e, self._cplex._lp,
self._conv(arg_list[0]),
self._cplex.variables._conv(arg_list[1]),
arg_list[2])
[docs]
def get_rhs(self, *args):
"""Returns the righthand side of constraints from the problem.
Can be called by four forms.
linear_constraints.get_rhs()
return the righthand side of all linear constraints from
the problem.
linear_constraints.get_rhs(i)
i must be a linear constraint name or index. Returns the
righthand side of the linear constraint whose index or
name is i.
linear_constraints.get_rhs(s)
s must be a sequence of linear constraint names or indices.
Returns the righthand side of the linear constraints with
indices the members of s. Equivalent to
[linear_constraints.get_rhs(i) for i in s]
linear_constraints.get_rhs(begin, end)
begin and end must be linear constraint indices or linear
constraint names. Returns the righthand side of the linear
constraints with indices between begin and end, inclusive
of end. Equivalent to
linear_constraints.get_rhs(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(rhs = [1.5 * i for i in range(10)],
names = [str(i) for i in range(10)])
>>> c.linear_constraints.get_num()
10
>>> c.linear_constraints.get_rhs(8)
12.0
>>> c.linear_constraints.get_rhs("1",3)
[1.5, 3.0, 4.5]
>>> c.linear_constraints.get_rhs([2,"0",5])
[3.0, 0.0, 7.5]
>>> c.linear_constraints.get_rhs()
[0.0, 1.5, 3.0, 4.5, 6.0, 7.5, 9.0, 10.5, 12.0, 13.5]
"""
def getrhs(a, b=self.get_num() - 1):
return CPX_PROC.getrhs(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(getrhs, self._conv, args)
[docs]
def get_senses(self, *args):
"""Returns the senses of constraints from the problem.
Can be called by four forms.
linear_constraints.get_senses()
return the senses of all linear constraints from the
problem.
linear_constraints.get_senses(i)
i must be a linear constraint name or index. Returns the
sense of the linear constraint whose index or name is i.
linear_constraints.get_senses(s)
s must be a sequence of linear constraint names or indices.
Returns the senses of the linear constraints with indices
the members of s. Equivalent to
[linear_constraints.get_senses(i) for i in s]
linear_constraints.get_senses(begin, end)
begin and end must be linear constraint indices or linear
constraint names. Returns the senses of the linear constraints
with indices between begin and end, inclusive of end.
Equivalent to
linear_constraints.get_senses(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(
... senses=["E", "G", "L", "R"],
... names=[str(i) for i in range(4)])
>>> c.linear_constraints.get_num()
4
>>> c.linear_constraints.get_senses(1)
'G'
>>> c.linear_constraints.get_senses("1",3)
['G', 'L', 'R']
>>> c.linear_constraints.get_senses([2,"0",1])
['L', 'E', 'G']
>>> c.linear_constraints.get_senses()
['E', 'G', 'L', 'R']
"""
def getsense(a, b=self.get_num() - 1):
return CPX_PROC.getsense(self._env._e, self._cplex._lp, a, b)
s = [i for i in "".join(apply_freeform_two_args(
getsense, self._conv, args))]
return s[0] if len(s) == 1 else s
[docs]
def get_range_values(self, *args):
"""Returns the range values of linear constraints from the problem.
That is, this method returns the lefthand side (lhs) for each
ranged constraint of the form lhs <= lin_expr <= rhs. This method
makes sense only for ranged constraints, that is, linear constraints
of sense 'R'.
The range values are a list of floats, specifying the difference
between lefthand side and righthand side of each linear constraint.
If range_values[i] > 0 (zero) then the constraint i is defined as
rhs[i] <= rhs[i] + range_values[i]. If range_values[i] < 0 (zero)
then constraint i is defined as
rhs[i] + range_value[i] <= a*x <= rhs[i].
Can be called by four forms.
linear_constraints.get_range_values()
return the range values of all linear constraints from the
problem.
linear_constraints.get_range_values(i)
i must be a linear constraint name or index. Returns the
range value of the linear constraint whose index or name is i.
linear_constraints.get_range_values(s)
s must be a sequence of linear constraint names or indices.
Returns the range values of the linear constraints with
indices the members of s. Equivalent to
[linear_constraints.get_range_values(i) for i in s]
linear_constraints.get_range_values(begin, end)
begin and end must be linear constraint indices or linear
constraint names. Returns the range values of the linear
constraints with indices between begin and end, inclusive
of end. Equivalent to
linear_constraints.get_range_values(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(
range_values = [1.5 * i for i in range(10)],
senses = ["R"] * 10,
names = [str(i) for i in range(10)])
>>> c.linear_constraints.get_num()
10
>>> c.linear_constraints.get_range_values(8)
12.0
>>> c.linear_constraints.get_range_values("1",3)
[1.5, 3.0, 4.5]
>>> c.linear_constraints.get_range_values([2,"0",5])
[3.0, 0.0, 7.5]
>>> c.linear_constraints.get_range_values()
[0.0, 1.5, 3.0, 4.5, 6.0, 7.5, 9.0, 10.5, 12.0, 13.5]
"""
def getrngval(a, b=self.get_num() - 1):
return CPX_PROC.getrngval(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(getrngval, self._conv, args)
[docs]
def get_coefficients(self, *args):
"""Returns coefficients by row, column coordinates.
There are two forms by which
linear_constraints.get_coefficients may be called.
linear_constraints.get_coefficients(row, col)
returns the coefficient.
linear_constraints.get_coefficients(sequence_of_pairs)
returns a list of coefficients.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["x0", "x1"])
>>> indices = c.linear_constraints.add(
names = ["c0", "c1"],
lin_expr = [[[1], [1.0]], [[0, 1], [2.0, -1.0]]])
>>> c.linear_constraints.get_coefficients("c0", "x1")
1.0
>>> c.linear_constraints.get_coefficients([("c1", "x0"), ("c1", "x1")])
[2.0, -1.0]
"""
def getcoef(row, col):
return CPX_PROC.getcoef(self._env._e, self._cplex._lp, row, col)
if len(args) == 2:
return getcoef(self._conv(args[0]),
self._cplex.variables._conv(args[1]))
if len(args) == 1:
return [self.get_coefficients(*arg) for arg in args[0]]
raise WrongNumberOfArgumentsError()
[docs]
def get_rows(self, *args):
"""Returns a set of rows of the linear constraint matrix.
Returns a list of SparsePair instances or a single SparsePair
instance, depending on the form by which it was called.
There are four forms by which linear_constraints.get_rows may be
called.
linear_constraints.get_rows()
return the entire linear constraint matrix.
linear_constraints.get_rows(i)
i must be a row name or index. Returns the ith row of
the linear constraint matrix.
linear_constraints.get_rows(s)
s must be a sequence of row names or indices. Returns the
rows of the linear constraint matrix indexed by the members
of s. Equivalent to
[linear_constraints.get_rows(i) for i in s]
linear_constraints.get_rows(begin, end)
begin and end must be row indices or row names. Returns the
rows of the linear constraint matrix with indices between begin
and end, inclusive of end. Equivalent to
linear_constraints.get_rows(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=["x1", "x2", "x3"])
>>> indices = c.linear_constraints.add(
... names=["c0", "c1", "c2", "c3"],
... lin_expr=[
... cplex.SparsePair(ind=["x1", "x3"], val=[1.0, -1.0]),
... cplex.SparsePair(ind=["x1", "x2"], val=[1.0, 1.0]),
... cplex.SparsePair(ind=["x1", "x2", "x3"], val=[-1.0] * 3),
... cplex.SparsePair(ind=["x2", "x3"], val=[10.0, -2.0])
... ]
... )
>>> c.linear_constraints.get_rows(0)
SparsePair(ind = [0, 2], val = [1.0, -1.0])
>>> for row in c.linear_constraints.get_rows(1,3):
... print(row)
SparsePair(ind = [0, 1], val = [1.0, 1.0])
SparsePair(ind = [0, 1, 2], val = [-1.0, -1.0, -1.0])
SparsePair(ind = [1, 2], val = [10.0, -2.0])
>>> for row in c.linear_constraints.get_rows(["c2", 0]):
... print(row)
SparsePair(ind = [0, 1, 2], val = [-1.0, -1.0, -1.0])
SparsePair(ind = [0, 2], val = [1.0, -1.0])
>>> for row in c.linear_constraints.get_rows():
... print(row)
SparsePair(ind = [0, 2], val = [1.0, -1.0])
SparsePair(ind = [0, 1], val = [1.0, 1.0])
SparsePair(ind = [0, 1, 2], val = [-1.0, -1.0, -1.0])
SparsePair(ind = [1, 2], val = [10.0, -2.0])
"""
def getrows(begin, end=self.get_num() - 1):
mat = _HBMatrix()
t = CPX_PROC.getrows(self._env._e, self._cplex._lp, begin, end)
mat.matbeg = t[0]
mat.matind = t[1]
mat.matval = t[2]
return [m for m in mat]
return apply_freeform_two_args(getrows, self._conv, args)
[docs]
def get_num_nonzeros(self):
"""Returns the number of nonzeros in the linear constraint
matrix.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=["x1", "x2", "x3"])
>>> indices = c.linear_constraints.add(
... names=["c0", "c1", "c2", "c3"],
... lin_expr=[
... cplex.SparsePair(ind=["x1", "x3"], val=[1.0, -1.0]),
... cplex.SparsePair(ind=["x1", "x2"], val=[1.0, 1.0]),
... cplex.SparsePair(ind=["x1", "x2", "x3"], val=[-1.0] * 3),
... cplex.SparsePair(ind=["x2", "x3"], val=[10.0, -2.0])
... ]
... )
>>> c.linear_constraints.get_num_nonzeros()
9
"""
return CPX_PROC.getnumnz(self._env._e, self._cplex._lp)
[docs]
def get_names(self, *args):
"""Returns the names of linear constraints from the problem.
There are four forms by which linear_constraints.get_names may be called.
linear_constraints.get_names()
return the names of all linear constraints from the problem.
linear_constraints.get_names(i)
i must be a linear constraint index. Returns the name of row i.
linear_constraints.get_names(s)
s must be a sequence of row indices. Returns the names of
the linear constraints with indices the members of s.
Equivalent to [linear_constraints.get_names(i) for i in s]
linear_constraints.get_names(begin, end)
begin and end must be linear constraint indices. Returns the
names of the linear constraints with indices between begin and
end, inclusive of end. Equivalent to
linear_constraints.get_names(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.linear_constraints.add(names = ["c" + str(i) for i in range(10)])
>>> c.linear_constraints.get_num()
10
>>> c.linear_constraints.get_names(8)
'c8'
>>> c.linear_constraints.get_names(1, 3)
['c1', 'c2', 'c3']
>>> c.linear_constraints.get_names([2, 0, 5])
['c2', 'c0', 'c5']
>>> c.linear_constraints.get_names()
['c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9']
"""
def getname(a, b=self.get_num() - 1):
return CPX_PROC.getrowname(self._env._e, self._cplex._lp,
a, b)
return apply_freeform_two_args(getname, self._conv, args)
[docs]
def get_histogram(self):
"""Returns a histogram of the rows of the linear constraint matrix.
To access the number of rows with given nonzero counts, use
slice notation. If a negative nonzero count is queried in
this manner an IndexError will be raised.
The __str__ method of the `Histogram` object returns a string
displaying the number of rows with given nonzeros counts in
human readable form.
The data member "orientation" of the histogram object is
"row", indicating that the histogram shows the nonzero
counts for the rows of the linear constraint matrix.
>>> import cplex
>>> c = cplex.Cplex("ind.lp")
>>> histogram = c.linear_constraints.get_histogram()
>>> print(histogram)
Row counts (excluding fixed variables):
<BLANKLINE>
Nonzero Count: 3 4 5 10 37
Number of Rows: 1 9 1 4 1
<BLANKLINE>
<BLANKLINE>
>>> histogram[4]
9
>>> histogram[2:7]
[0, 1, 9, 1, 0]
"""
return Histogram(self._cplex, "row")
[docs]
class IndicatorType(ConstantClass):
"""Identifiers for types of indicator constraints."""
if_ = _constants.CPX_INDICATOR_IF
"""CPX_INDICATOR_IF ('->')."""
onlyif = _constants.CPX_INDICATOR_ONLYIF
"""CPX_INDICATOR_ONLYIF ('<-')"""
iff = _constants.CPX_INDICATOR_IFANDONLYIF
"""CPX_INDICATOR_IFANDONLYIF ('<->')"""
[docs]
class IndicatorConstraintInterface(BaseInterface):
"""Methods for adding, modifying, and querying indicator constraints."""
type_ = IndicatorType()
"""See `IndicatorType()`"""
[docs]
def __init__(self, cplex):
"""Creates a new IndicatorConstraintInterface.
The indicator constraints interface is exposed by the top-level
`Cplex` class as `Cplex.indicator_constraints`. This constructor
is not meant to be used externally.
"""
super().__init__(cplex=cplex,
getindexfunc=CPX_PROC.getindconstrindex)
[docs]
def get_num(self):
"""Returns the number of indicator constraints.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.indicator_constraints.add(name="ind1")
0
>>> c.indicator_constraints.get_num()
1
"""
return CPX_PROC.getnumindconstrs(self._env._e, self._cplex._lp)
def _add_batch(self, lin_expr, sense, rhs, indvar, complemented, name,
indtype):
if not isinstance(sense, str):
sense = "".join(sense)
arg_list = [lin_expr, sense, rhs, indvar, complemented, name,
indtype]
num_new_rows = max_arg_length(arg_list)
validate_arg_lengths(
arg_list,
": lin_expr, sense, rhs, indvar, complemented, name, indtype"
)
with CPX_PROC.chbmatrix(lin_expr, self._cplex._env_lp_ptr,
0) as (linmat, nnz):
CPX_PROC.addindconstr(self._env._e, self._cplex._lp,
num_new_rows,
self._cplex.variables._conv(indvar),
complemented, rhs, sense,
linmat, indtype, name, nnz)
[docs]
def add_batch(self, lin_expr=None, sense=None, rhs=None, indvar=None,
complemented=None, name=None, indtype=None):
"""Adds indicator constraints to the problem.
Takes up to eight keyword arguments.
If more than one argument is specified, all arguments must
have the same length.
lin_expr : either a list of SparsePair instances or a matrix in
list-of-lists format.
Note
lin_expr must not contain duplicate indices. If lin_expr
references a variable more than once, either by index, name,
or a combination of index and name, an exception will be
raised.
sense : must be either a list of single-character strings or a
string containing the senses of the indicator constraints.
Each entry must be one of 'G', 'L', 'E', indicating
greater-than-or-equal-to (>=), less-than-or-equal-to (<=), and
equality (=), respectively. Left unspecified, the default is 'E'.
rhs : a list of floats, specifying the righthand side of each
indicator constraint.
indvar : a list of names or indices (or a mixture of the two), of
the variables that control whether the constraint is active or
not.
complemented : a list of values (0 or 1). Default value of 0
instructs CPLEX to interpret indicator constraint as active when
the indicator variable is 1. Set complemented to 1 to instruct
CPLEX that the indicator constraint is active when indvar = 0.
name : a list of strings that determine the names of the
individual constraints.
indtype : a list of the types of indicator constraints. Defaults
to CPX_INDICATOR_IF ('->'). See `IndicatorType()`.
Returns an iterator containing the indices of the added indicator
constraints.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=["x1", "x2", "x3"])
>>> indices = c.indicator_constraints.add_batch(
... lin_expr=[cplex.SparsePair(ind=["x2"], val=[2.0]),
... cplex.SparsePair(ind=["x3"], val=[2.0])],
... sense="LL",
... rhs=[1.0, 1.0],
... indvar=["x1", "x2"],
... complemented=[0, 0],
... name=["ind1", "ind2"],
... indtype=[c.indicator_constraints.type_.if_,
... c.indicator_constraints.type_.if_])
>>> len(list(indices))
2
"""
(lin_expr, sense, rhs, indvar,
complemented, name, indtype) = init_list_args(
lin_expr, sense, rhs, indvar, complemented, name, indtype)
return self._add_iter(self.get_num, self._add_batch,
lin_expr, sense, rhs, indvar, complemented,
name, indtype)
def _add(self, lin_expr, sense, rhs, indvar, complemented, name,
indtype):
"""non-public"""
self._add_batch([lin_expr], sense, [rhs], [indvar], [complemented],
[name], [indtype])
[docs]
def add(self, lin_expr=None, sense="E", rhs=0.0, indvar=0,
complemented=0, name="", indtype=IndicatorType.if_):
"""Adds an indicator constraint to the problem.
Takes up to eight keyword arguments.
lin_expr : either a SparsePair or a list of two lists, the first of
which contains variable indices or names, the second of which
contains values.
Note
lin_expr must not contain duplicate indices. If lin_expr
references a variable more than once, either by index, name,
or a combination of index and name, an exception will be
raised.
sense : the sense of the constraint, may be "L", "G", or "E":
default is "E"
rhs : a float defining the righthand side of the constraint
indvar : the name or index of the variable that controls if
the constraint is active
complemented : default value of 0 instructs CPLEX to interpret
indicator constraint as active when the indicator variable is 1.
Set complemented to 1 to instruct CPLEX that the indicator
constraint is active when indvar = 0.
name : the name of the constraint.
indtype : the type of indicator constraint. Defaults to
CPX_INDICATOR_IF ('->'). See `IndicatorType()`.
Returns the index of the added indicator constraint.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["x1", "x2"])
>>> c.indicator_constraints.add(
... indvar="x1",
... complemented=0,
... rhs=1.0,
... sense="G",
... lin_expr=cplex.SparsePair(ind=["x2"], val=[2.0]),
... name="ind1",
... indtype=c.indicator_constraints.type_.if_)
0
"""
if lin_expr is None:
lin_expr = SparsePair()
# We only ever create one indicator constraint at a time.
return self._add_single(self.get_num, self._add, lin_expr,
sense, rhs, indvar, complemented,
name, indtype)
[docs]
def delete(self, *args):
"""Deletes indicator constraints from the problem.
There are four forms by which indicator_constraints.delete may be
called.
indicator_constraints.delete()
deletes all indicator constraints from the problem.
indicator_constraints.delete(i)
i must be an indicator constraint name or index. Deletes
the indicator constraint whose index or name is i.
indicator_constraints.delete(s)
s must be a sequence of indicator constraint names or
indices. Deletes the indicator constraints with names or
indices contained within s. Equivalent to
[indicator_constraints.delete(i) for i in s].
indicator_constraints.delete(begin, end)
begin and end must be indicator constraint indices or indicator
constraint names. Deletes the indicator constraints with
indices between begin and end, inclusive of end. Equivalent to
indicator_constraints.delete(range(begin, end + 1)). This will
give the best performance when deleting batches of indicator
constraints.
See `CPXdelindconstrs <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/mipapi/delindconstrs.html>`_ in the Callable Library Reference
Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> [c.indicator_constraints.add(name=str(i)) for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.indicator_constraints.get_num()
10
>>> c.indicator_constraints.delete(8)
>>> c.indicator_constraints.get_names()
['0', '1', '2', '3', '4', '5', '6', '7', '9']
>>> c.indicator_constraints.delete("1", 3)
>>> c.indicator_constraints.get_names()
['0', '4', '5', '6', '7', '9']
>>> c.indicator_constraints.delete([2, "0", 5])
>>> c.indicator_constraints.get_names()
['4', '6', '7']
>>> c.indicator_constraints.delete()
>>> c.indicator_constraints.get_names()
[]
"""
def _delete(begin, end=None):
CPX_PROC.delindconstrs(self._env._e, self._cplex._lp, begin, end)
delete_set_by_range(_delete, self._conv, self.get_num(), *args)
[docs]
def get_indicator_variables(self, *args):
"""Returns the indicator variables of a set of indicator constraints.
May be called by four forms.
indicator_constraints.get_indicator_variables()
return the indicator variables of all indicator constraints
from the problem.
indicator_constraints.get_indicator_variables(i)
i must be an indicator constraint name or index. Returns the
indicator variables of the indicator constraint whose index
or name is i.
indicator_constraints.get_indicator_variables(s)
s must be a sequence of indicator constraint names or
indices. Returns the indicator variables of the indicator
constraints with indices the members of s. Equivalent to
[indicator_constraints.get_indicator_variables(i) for i in s]
indicator_constraints.get_indicator_variables(begin, end)
begin and end must be indicator constraint indices or indicator
constraint names. Returns the indicator variables of the
indicator constraints with indices between begin and end,
inclusive of end. Equivalent to
indicator_constraints.get_indicator_variables(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(11)], types = "B" * 11)
>>> [c.indicator_constraints.add(
... name=str(i), indvar=i,
... lin_expr=cplex.SparsePair(ind=[i+1], val=[1.0]))
... for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.indicator_constraints.get_num()
10
>>> c.indicator_constraints.get_indicator_variables(8)
8
>>> c.indicator_constraints.get_indicator_variables("1",3)
[1, 2, 3]
>>> c.indicator_constraints.get_indicator_variables([2,"0",5])
[2, 0, 5]
>>> c.indicator_constraints.get_indicator_variables()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
"""
def getindvar(begin, end=self.get_num() - 1):
return CPX_PROC.getindconstr_constant(
self._env._e, self._cplex._lp, begin, end)[1]
return apply_freeform_two_args(getindvar, self._conv, args)
[docs]
def get_complemented(self, *args):
"""Returns whether a set of indicator constraints is complemented.
May be called by four forms.
indicator_constraints.get_complemented()
return whether or not all indicator constraints from the
problem are complemented.
indicator_constraints.get_complemented(i)
i must be an indicator constraint name or index. Returns
whether or not the indicator constraint whose index or name
is i is complemented.
indicator_constraints.get_complemented(s)
s must be a sequence of indicator constraint names or
indices. Returns whether or not the indicator constraints
with indices the members of s are complemented. Equivalent
to [indicator_constraints.get_complemented(i) for i in s]
indicator_constraints.get_complemented(begin, end)
begin and end must be indicator constraint indices or indicator
constraint names. Returns whether or not the indicator
constraints with indices between begin and end, inclusive of
end, are complemented. Equivalent to
indicator_constraints.get_complemented(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(11)], types = "B" * 11)
>>> [c.indicator_constraints.add(
... name=str(i), indvar=10,
... complemented=i % 2)
... for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.indicator_constraints.get_num()
10
>>> c.indicator_constraints.get_complemented(8)
0
>>> c.indicator_constraints.get_complemented("1",3)
[1, 0, 1]
>>> c.indicator_constraints.get_complemented([2,"0",5])
[0, 0, 1]
>>> c.indicator_constraints.get_complemented()
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
"""
def getcomp(begin, end=self.get_num() - 1):
return CPX_PROC.getindconstr_constant(
self._env._e, self._cplex._lp, begin, end)[2]
return apply_freeform_two_args(getcomp, self._conv, args)
[docs]
def get_num_nonzeros(self, *args):
"""Returns the number of nonzeros in a set of indicator constraints.
May be called by four forms.
indicator_constraints.get_num_nonzeros()
return the number of nonzeros in all indicator constraints
from the problem.
indicator_constraints.get_num_nonzeros(i)
i must be an indicator constraint name or index. Returns the
number of nonzeros in the indicator constraint whose index
or name is i.
indicator_constraints.get_num_nonzeros(s)
s must be a sequence of indicator constraint names or
indices. Returns the number of nonzeros in the indicator
constraints with indices the members of s. Equivalent to
[indicator_constraints.get_num_nonzeros(i) for i in s]
indicator_constraints.get_num_nonzeros(begin, end)
begin and end must be indicator constraint indices or indicator
constraint names. Returns the number of nonzeros in the
indicator constraints with indices between begin and end,
inclusive of end. Equivalent to
indicator_constraints.get_num_nonzeros(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(11)], types = "B" * 11)
>>> [c.indicator_constraints.add(
... name=str(i), indvar=10,
... lin_expr=[range(i), [1.0 * (j+1.0) for j in range(i)]])
... for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.indicator_constraints.get_num()
10
>>> c.indicator_constraints.get_num_nonzeros(8)
8
>>> c.indicator_constraints.get_num_nonzeros("1",3)
[1, 2, 3]
>>> c.indicator_constraints.get_num_nonzeros([2,"0",5])
[2, 0, 5]
>>> c.indicator_constraints.get_num_nonzeros()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
"""
def getnnz(a):
# NB: We return surplus here for nzcnt (this is on purpose).
return CPX_PROC.getindconstr_constant(
self._env._e, self._cplex._lp, a, a)[5]
return apply_freeform_one_arg(
getnnz, self._conv, self.get_num(), args)
[docs]
def get_rhs(self, *args):
"""Returns the righthand side of a set of indicator constraints.
May be called by four forms.
indicator_constraints.get_rhs()
return the righthand side of all indicator constraints
from the problem.
indicator_constraints.get_rhs(i)
i must be an indicator constraint name or index. Returns the
righthand side of the indicator constraint whose index or
name is i.
indicator_constraints.get_rhs(s)
s must be a sequence of indicator constraint names or
indices. Returns the righthand side of the indicator
constraints with indices the members of s. Equivalent to
[indicator_constraints.get_rhs(i) for i in s]
indicator_constraints.get_rhs(begin, end)
begin and end must be indicator constraint indices or indicator
constraint names. Returns the righthand side of the indicator
constraints with indices between begin and end, inclusive of
end. Equivalent to
indicator_constraints.get_rhs(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> [c.indicator_constraints.add(rhs=1.5 * i, name=str(i)) for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.indicator_constraints.get_num()
10
>>> c.indicator_constraints.get_rhs(8)
12.0
>>> c.indicator_constraints.get_rhs("1",3)
[1.5, 3.0, 4.5]
>>> c.indicator_constraints.get_rhs([2,"0",5])
[3.0, 0.0, 7.5]
>>> c.indicator_constraints.get_rhs()
[0.0, 1.5, 3.0, 4.5, 6.0, 7.5, 9.0, 10.5, 12.0, 13.5]
"""
def getrhs(begin, end=self.get_num() - 1):
return CPX_PROC.getindconstr_constant(
self._env._e, self._cplex._lp, begin, end)[3]
return apply_freeform_two_args(getrhs, self._conv, args)
[docs]
def get_senses(self, *args):
"""Returns the sense of a set of indicator constraints.
May be called by four forms.
indicator_constraints.get_senses()
return the senses of all indicator constraints from the
problem.
indicator_constraints.get_senses(i)
i must be an indicator constraint name or index. Returns the
sense of the indicator constraint whose index or name is i.
indicator_constraints.get_senses(s)
s must be a sequence of indicator constraint names or
indices. Returns the senses of the indicator constraints
with indices the members of s. Equivalent to
[indicator_constraints.get_senses(i) for i in s]
indicator_constraints.get_senses(begin, end)
begin and end must be indicator constraint indices or indicator
constraint names. Returns the senses of the indicator
constraints with indices between begin and end, inclusive of
end. Equivalent to
indicator_constraints.get_senses(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> [c.indicator_constraints.add(name=str(i), sense=j)
... for i, j in enumerate("EGLE")]
[0, 1, 2, 3]
>>> c.indicator_constraints.get_num()
4
>>> c.indicator_constraints.get_senses(1)
'G'
>>> c.indicator_constraints.get_senses("1",3)
['G', 'L', 'E']
>>> c.indicator_constraints.get_senses([2,"0",1])
['L', 'E', 'G']
>>> c.indicator_constraints.get_senses()
['E', 'G', 'L', 'E']
"""
def getsense(begin, end=self.get_num() - 1):
return CPX_PROC.getindconstr_constant(
self._env._e, self._cplex._lp, begin, end)[4]
result = apply_freeform_two_args(getsense, self._conv, args)
s = [i for i in "".join(result)]
return s[0] if len(s) == 1 else s
[docs]
def get_types(self, *args):
"""Returns the type of a set of indicator constraints.
See `IndicatorType()`.
May be called by four forms.
indicator_constraints.get_types()
return the types of all indicator constraints from the
problem.
indicator_constraints.get_types(i)
i must be an indicator constraint name or index. Returns the
type of the indicator constraint whose index or name is i.
indicator_constraints.get_types(s)
s must be a sequence of indicator constraint names or
indices. Returns the types of the indicator constraints
with indices the members of s. Equivalent to
[indicator_constraints.get_types(i) for i in s]
indicator_constraints.get_types(begin, end)
begin and end must be indicator constraint indices or indicator
constraint names. Returns the types of the indicator
constraints with indices between begin and end, inclusive of
end. Equivalent to
indicator_constraints.get_types(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> idx = c.indicator_constraints.add(name='i1')
>>> c.indicator_constraints.get_types(idx)
1
>>> c.indicator_constraints.type_[1]
'if_'
"""
def gettype(begin, end=self.get_num() - 1):
return CPX_PROC.getindconstr_constant(
self._env._e, self._cplex._lp, begin, end)[0]
return apply_freeform_two_args(gettype, self._conv, args)
[docs]
def get_linear_components(self, *args):
"""Returns the linear constraint of a set of indicator
constraints.
Returns a list of SparsePair instances or a single SparsePair
instance, depending on the form by which it was called.
May be called by four forms.
indicator_constraints.get_linear_components()
return the linear components of all indicator constraints
from the problem.
indicator_constraints.get_linear_components(i)
i must be an indicator constraint name or index. Returns the
linear component of the indicator constraint whose index or
name is i.
indicator_constraints.get_linear_components(s)
s must be a sequence of indicator constraint names or
indices. Returns the linear components of the indicator
constraints with indices the members of s. Equivalent to
[indicator_constraints.get_linear_components(i) for i in s]
indicator_constraints.get_linear_components(begin, end)
begin and end must be indicator constraint indices or indicator
constraint names. Returns the linear components of the
indicator constraints with indices between begin and end,
inclusive of end. Equivalent to
indicator_constraints.get_linear_components(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names=[str(i) for i in range(4)],
... types="B" * 4
... )
>>> [c.indicator_constraints.add(
... name=str(i), indvar=3,
... lin_expr=[range(i), [1.0 * (j+1.0) for j in range(i)]])
... for i in range(3)]
[0, 1, 2]
>>> c.indicator_constraints.get_num()
3
>>> c.indicator_constraints.get_linear_components(2)
SparsePair(ind = [0, 1], val = [1.0, 2.0])
>>> for row in c.indicator_constraints.get_linear_components("0", 1):
... print(row)
SparsePair(ind = [], val = [])
SparsePair(ind = [0], val = [1.0])
>>> for row in c.indicator_constraints.get_linear_components([1, "0"]):
... print(row)
SparsePair(ind = [0], val = [1.0])
SparsePair(ind = [], val = [])
>>> for row in c.indicator_constraints.get_linear_components():
... print(row)
SparsePair(ind = [], val = [])
SparsePair(ind = [0], val = [1.0])
SparsePair(ind = [0, 1], val = [1.0, 2.0])
"""
def getlin(begin, end=self.get_num() - 1):
mat = _HBMatrix()
mat.matbeg, mat.matind, mat.matval = CPX_PROC.getindconstr(
self._env._e, self._cplex._lp, begin, end)
return [m for m in mat]
return apply_freeform_two_args(getlin, self._conv, args)
[docs]
def get_names(self, *args):
"""Returns the names of a set of indicator constraints.
May be called by four forms.
indicator_constraints.get_names()
return the names of all indicator constraints from the
problem.
indicator_constraints.get_names(i)
i must be an indicator constraint index. Returns the name
of constraint i.
indicator_constraints.get_names(s)
s must be a sequence of indicator constraint indices.
Returns the names of the indicator constraints with indices
the members of s. Equivalent to
[indicator_constraints.get_names(i) for i in s]
indicator_constraints.get_names(begin, end)
begin and end must be indicator constraint indices. Returns the
names of the indicator constraints with indices between begin
and end, inclusive of end. Equivalent to
indicator_constraints.get_names(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> [c.indicator_constraints.add(name="i" + str(i))
... for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.indicator_constraints.get_num()
10
>>> c.indicator_constraints.get_names(8)
'i8'
>>> c.indicator_constraints.get_names(1, 3)
['i1', 'i2', 'i3']
>>> c.indicator_constraints.get_names([2, 0, 5])
['i2', 'i0', 'i5']
>>> c.indicator_constraints.get_names()
['i0', 'i1', 'i2', 'i3', 'i4', 'i5', 'i6', 'i7', 'i8', 'i9']
"""
def getname(a):
return CPX_PROC.getindconstrname(
self._env._e, self._cplex._lp, a)
return apply_freeform_one_arg(
getname, self._conv, self.get_num(), args)
[docs]
class QuadraticConstraintInterface(BaseInterface):
"""Methods for adding, modifying, and querying quadratic constraints."""
[docs]
def __init__(self, cplex):
"""Creates a new QuadraticConstraintInterface.
The quadratic constraints interface is exposed by the top-level
`Cplex` class as `Cplex.quadratic_constraints`. This constructor
is not meant to be used externally.
"""
super().__init__(cplex=cplex, getindexfunc=CPX_PROC.getqconstrindex)
[docs]
def get_num(self):
"""Returns the number of quadratic constraints.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ['x','y'])
>>> l = cplex.SparsePair(ind = ['x'], val = [1.0])
>>> q = cplex.SparseTriple(ind1 = ['x'], ind2 = ['y'], val = [1.0])
>>> [c.quadratic_constraints.add(name=str(i), lin_expr=l, quad_expr=q)
... for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.quadratic_constraints.get_num()
10
"""
return CPX_PROC.getnumqconstrs(self._env._e, self._cplex._lp)
def _add(self, lin_expr, quad_expr, sense, rhs, name):
"""non-public"""
ind, val = unpack_pair(lin_expr)
if len(val) == 1 and val[0] == 0.0:
ind = []
val = []
ind1, ind2, qval = unpack_triple(quad_expr)
varcache = {}
CPX_PROC.addqconstr(self._env._e, self._cplex._lp, rhs, sense,
self._cplex.variables._conv(ind, varcache),
val,
self._cplex.variables._conv(ind1, varcache),
self._cplex.variables._conv(ind2, varcache),
qval, name)
[docs]
def add(self, lin_expr=None, quad_expr=None, sense="L", rhs=0.0, name=""):
"""Adds a quadratic constraint to the problem.
Takes up to five keyword arguments:
lin_expr : either a SparsePair or a list of two lists specifying
the linear component of the constraint.
Note
lin_expr must not contain duplicate indices. If lin_expr
references a variable more than once, either by index, name,
or a combination of index and name, an exception will be
raised.
quad_expr : either a SparseTriple or a list of three lists
specifying the quadratic component of the constraint.
Note
quad_expr must not contain duplicate indices. If quad_expr
references a matrix entry more than once, either by indices,
names, or a combination of indices and names, an exception
will be raised.
sense : either "L", "G", or "E"
rhs : a float specifying the righthand side of the constraint.
name : the name of the constraint.
Returns the index of the added quadratic constraint.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ['x','y'])
>>> l = cplex.SparsePair(ind = ['x'], val = [1.0])
>>> q = cplex.SparseTriple(ind1 = ['x'], ind2 = ['y'], val = [1.0])
>>> c.quadratic_constraints.add(name = "my_quad",
... lin_expr = l,
... quad_expr = q,
... rhs = 1.0,
... sense = "G")
0
"""
if lin_expr is None:
lin_expr = SparsePair([0], [0.0])
if quad_expr is None:
quad_expr = SparseTriple([0], [0], [0.0])
# We only ever create one quadratic constraint at a time.
return self._add_single(self.get_num, self._add,
lin_expr, quad_expr, sense, rhs, name)
[docs]
def delete(self, *args):
"""Deletes quadratic constraints from the problem.
There are four forms by which quadratic_constraints.delete may be
called.
quadratic_constraints.delete()
deletes all quadratic constraints from the problem.
quadratic_constraints.delete(i)
i must be a quadratic constraint name or index. Deletes
the quadratic constraint whose index or name is i.
quadratic_constraints.delete(s)
s must be a sequence of quadratic constraint names or
indices. Deletes the quadratic constraints with names or
indices contained within s. Equivalent to
[quadratic_constraints.delete(i) for i in s].
quadratic_constraints.delete(begin, end)
begin and end must be quadratic constraint indices or quadratic
constraint names. Deletes the quadratic constraints with
indices between begin and end, inclusive of end. Equivalent to
quadratic_constraints.delete(range(begin, end + 1)). This will
give the best performance when deleting batches of quadratic
constraints.
See `CPXdelqconstrs <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/socpapi/delqconstrs.html>`_ in the Callable Library Reference
Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=['x', 'y'])
>>> l = cplex.SparsePair(ind=['x'], val=[1.0])
>>> q = cplex.SparseTriple(ind1=['x'], ind2=['y'], val=[1.0])
>>> [c.quadratic_constraints.add(
... name=str(i), lin_expr=l, quad_expr=q)
... for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.quadratic_constraints.get_num()
10
>>> c.quadratic_constraints.delete(8)
>>> c.quadratic_constraints.get_names()
['0', '1', '2', '3', '4', '5', '6', '7', '9']
>>> c.quadratic_constraints.delete("1", 3)
>>> c.quadratic_constraints.get_names()
['0', '4', '5', '6', '7', '9']
>>> c.quadratic_constraints.delete([2, "0", 5])
>>> c.quadratic_constraints.get_names()
['4', '6', '7']
>>> c.quadratic_constraints.delete()
>>> c.quadratic_constraints.get_names()
[]
"""
def _delete(begin, end=None):
CPX_PROC.delqconstrs(self._env._e, self._cplex._lp, begin, end)
delete_set_by_range(_delete, self._conv, self.get_num(), *args)
[docs]
def get_rhs(self, *args):
"""Returns the righthand side of a set of quadratic constraints.
Can be called by four forms.
quadratic_constraints.get_rhs()
return the righthand side of all quadratic constraints
from the problem.
quadratic_constraints.get_rhs(i)
i must be a quadratic constraint name or index. Returns the
righthand side of the quadratic constraint whose index or
name is i.
quadratic_constraints.get_rhs(s)
s must be a sequence of quadratic constraint names or
indices. Returns the righthand side of the quadratic
constraints with indices the members of s. Equivalent to
[quadratic_constraints.get_rhs(i) for i in s]
quadratic_constraints.get_rhs(begin, end)
begin and end must be quadratic constraint indices or quadratic
constraint names. Returns the righthand side of the quadratic
constraints with indices between begin and end, inclusive of
end. Equivalent to
quadratic_constraints.get_rhs(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(10)])
>>> [c.quadratic_constraints.add(rhs=1.5 * i, name=str(i))
... for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.quadratic_constraints.get_num()
10
>>> c.quadratic_constraints.get_rhs(8)
12.0
>>> c.quadratic_constraints.get_rhs("1",3)
[1.5, 3.0, 4.5]
>>> c.quadratic_constraints.get_rhs([2,"0",5])
[3.0, 0.0, 7.5]
>>> c.quadratic_constraints.get_rhs()
[0.0, 1.5, 3.0, 4.5, 6.0, 7.5, 9.0, 10.5, 12.0, 13.5]
"""
def getrhs(a):
return CPX_PROC.getqconstr_info(
self._env._e, self._cplex._lp, a)[0]
return apply_freeform_one_arg(
getrhs, self._conv, self.get_num(), args)
[docs]
def get_senses(self, *args):
"""Returns the senses of a set of quadratic constraints.
Can be called by four forms.
quadratic_constraints.get_senses()
return the senses of all quadratic constraints from the
problem.
quadratic_constraints.get_senses(i)
i must be a quadratic constraint name or index. Returns the
sense of the quadratic constraint whose index or name is i.
quadratic_constraints.get_senses(s)
s must be a sequence of quadratic constraint names or
indices. Returns the senses of the quadratic constraints
with indices the members of s. Equivalent to
[quadratic_constraints.get_senses(i) for i in s]
quadratic_constraints.get_senses(begin, end)
begin and end must be quadratic constraint indices or quadratic
constraint names. Returns the senses of the quadratic
constraints with indices between begin and end, inclusive of
end. Equivalent to
quadratic_constraints.get_senses(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["x0"])
>>> [c.quadratic_constraints.add(name=str(i), sense=j)
... for i, j in enumerate("GGLL")]
[0, 1, 2, 3]
>>> c.quadratic_constraints.get_num()
4
>>> c.quadratic_constraints.get_senses(1)
'G'
>>> c.quadratic_constraints.get_senses("1",3)
['G', 'L', 'L']
>>> c.quadratic_constraints.get_senses([2,"0",1])
['L', 'G', 'G']
>>> c.quadratic_constraints.get_senses()
['G', 'G', 'L', 'L']
"""
def getsense(a):
return CPX_PROC.getqconstr_info(
self._env._e, self._cplex._lp, a)[1]
return apply_freeform_one_arg(
getsense, self._conv, self.get_num(), args)
[docs]
def get_linear_num_nonzeros(self, *args):
"""Returns the number of nonzeros in the linear part of a set of quadratic constraints.
Can be called by four forms.
quadratic_constraints.get_linear_num_nonzeros()
return the number of nonzeros in all quadratic constraints
from the problem.
quadratic_constraints.get_linear_num_nonzeros(i)
i must be a quadratic constraint name or index. Returns the
number of nonzeros in the quadratic constraint whose index
or name is i.
quadratic_constraints.get_linear_num_nonzeros(s)
s must be a sequence of quadratic constraint names or
indices. Returns the number of nonzeros in the quadratic
constraints with indices the members of s. Equivalent to
[quadratic_constraints.get_linear_num_nonzeros(i) for i in s]
quadratic_constraints.get_linear_num_nonzeros(begin, end)
begin and end must be quadratic constraint indices or quadratic
constraint names. Returns the number of nonzeros in the
quadratic constraints with indices between begin and end,
inclusive of end. Equivalent to
quadratic_constraints.get_linear_num_nonzeros(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(11)], types = "B" * 11)
>>> [c.quadratic_constraints.add(
... name = str(i),
... lin_expr = [range(i), [1.0 * (j+1.0) for j in range(i)]])
... for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.quadratic_constraints.get_num()
10
>>> c.quadratic_constraints.get_linear_num_nonzeros(8)
8
>>> c.quadratic_constraints.get_linear_num_nonzeros("1",3)
[1, 2, 3]
>>> c.quadratic_constraints.get_linear_num_nonzeros([2,"0",5])
[2, 0, 5]
>>> c.quadratic_constraints.get_linear_num_nonzeros()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
"""
def getlinnz(a):
return CPX_PROC.getqconstr_info(
self._env._e, self._cplex._lp, a)[2]
return apply_freeform_one_arg(
getlinnz, self._conv, self.get_num(), args)
[docs]
def get_linear_components(self, *args):
"""Returns the linear part of a set of quadratic constraints.
Returns a list of SparsePair instances or one SparsePair
instance.
Can be called by four forms.
quadratic_constraints.get_linear_components()
return the linear components of all quadratic constraints
from the problem.
quadratic_constraints.get_linear_components(i)
i must be a quadratic constraint name or index. Returns the
linear component of the quadratic constraint whose index or
name is i.
quadratic_constraints.get_linear_components(s)
s must be a sequence of quadratic constraint names or
indices. Returns the linear components of the quadratic
constraints with indices the members of s. Equivalent to
[quadratic_constraints.get_linear_components(i) for i in s]
quadratic_constraints.get_linear_components(begin, end)
begin and end must be quadratic constraint indices or quadratic
constraint names. Returns the linear components of the
quadratic constraints with indices between begin and end,
inclusive of end. Equivalent to
quadratic_constraints.get_linear_components(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names=[str(i) for i in range(4)],
... types="B" * 4
... )
>>> [c.quadratic_constraints.add(
... name=str(i),
... lin_expr=[range(i), [1.0 * (j+1.0) for j in range(i)]])
... for i in range(3)]
[0, 1, 2]
>>> c.quadratic_constraints.get_num()
3
>>> c.quadratic_constraints.get_linear_components(2)
SparsePair(ind = [0, 1], val = [1.0, 2.0])
>>> for row in c.quadratic_constraints.get_linear_components("0", 1):
... print(row)
SparsePair(ind = [], val = [])
SparsePair(ind = [0], val = [1.0])
>>> for row in c.quadratic_constraints.get_linear_components([1, "0"]):
... print(row)
SparsePair(ind = [0], val = [1.0])
SparsePair(ind = [], val = [])
>>> for row in c.quadratic_constraints.get_linear_components():
... print(row)
SparsePair(ind = [], val = [])
SparsePair(ind = [0], val = [1.0])
SparsePair(ind = [0, 1], val = [1.0, 2.0])
"""
def getlin(a):
return SparsePair(*CPX_PROC.getqconstr_lin(
self._env._e, self._cplex._lp, a))
return apply_freeform_one_arg(
getlin, self._conv, self.get_num(), args)
[docs]
def get_quad_num_nonzeros(self, *args):
"""Returns the number of nonzeros in the quadratic part of a set of quadratic constraints.
Can be called by four forms.
quadratic_constraints.get_quad_num_nonzeros()
Returns the number of nonzeros in all quadratic constraints
from the problem.
quadratic_constraints.get_quad_num_nonzeros(i)
i must be a quadratic constraint name or index. Returns the
number of nonzeros in the quadratic constraint whose index
or name is i.
quadratic_constraints.get_quad_num_nonzeros(s)
s must be a sequence of quadratic constraint names or
indices. Returns the number of nonzeros in the quadratic
constraints with indices the members of s. Equivalent to
[quadratic_constraints.get_quad_num_nonzeros(i) for i in s]
quadratic_constraints.get_quad_num_nonzeros(begin, end)
begin and end must be quadratic constraint indices or quadratic
constraint names. Returns the number of nonzeros in the
quadratic constraints with indices between begin and end,
inclusive of end. Equivalent to
quadratic_constraints.get_quad_num_nonzeros(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(11)])
>>> [c.quadratic_constraints.add(
... name = str(i),
... quad_expr = [range(i), range(i), [1.0 * (j+1.0) for j in range(i)]])
... for i in range(1, 11)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.quadratic_constraints.get_num()
10
>>> c.quadratic_constraints.get_quad_num_nonzeros(8)
9
>>> c.quadratic_constraints.get_quad_num_nonzeros("1",2)
[1, 2, 3]
>>> c.quadratic_constraints.get_quad_num_nonzeros([2,"1",5])
[3, 1, 6]
>>> c.quadratic_constraints.get_quad_num_nonzeros()
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
"""
def getquadnz(a):
return CPX_PROC.getqconstr_info(
self._env._e, self._cplex._lp, a)[3]
return apply_freeform_one_arg(
getquadnz, self._conv, self.get_num(), args)
[docs]
def get_quadratic_components(self, *args):
"""Returns the quadratic part of a set of quadratic constraints.
Can be called by four forms.
quadratic_constraints.get_quadratic_components()
return the quadratic components of all quadratic constraints
from the problem.
quadratic_constraints.get_quadratic_components(i)
i must be a quadratic constraint name or index. Returns the
quadratic component of the quadratic constraint whose index or
name is i.
quadratic_constraints.get_quadratic_components(s)
s must be a sequence of quadratic constraint names or
indices. Returns the quadratic components of the quadratic
constraints with indices the members of s. Equivalent to
[quadratic_constraints.get_quadratic_components(i) for i in s]
quadratic_constraints.get_quadratic_components(begin, end)
begin and end must be quadratic constraint indices or quadratic
constraint names. Returns the quadratic components of the
quadratic constraints with indices between begin and end,
inclusive of end. Equivalent to
quadratic_constraints.get_quadratic_components(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names=[str(i) for i in range(4)]
... )
>>> [c.quadratic_constraints.add(
... name="q{0}".format(i),
... quad_expr=[range(i), range(i),
... [1.0 * (j+1.0) for j in range(i)]])
... for i in range(1, 3)]
[0, 1]
>>> c.quadratic_constraints.get_num()
2
>>> c.quadratic_constraints.get_quadratic_components(1)
SparseTriple(ind1 = [0, 1], ind2 = [0, 1], val = [1.0, 2.0])
>>> for quad in c.quadratic_constraints.get_quadratic_components("q1", 1):
... print(quad)
SparseTriple(ind1 = [0], ind2 = [0], val = [1.0])
SparseTriple(ind1 = [0, 1], ind2 = [0, 1], val = [1.0, 2.0])
>>> for quad in c.quadratic_constraints.get_quadratic_components(["q2", 0]):
... print(quad)
SparseTriple(ind1 = [0, 1], ind2 = [0, 1], val = [1.0, 2.0])
SparseTriple(ind1 = [0], ind2 = [0], val = [1.0])
>>> for quad in c.quadratic_constraints.get_quadratic_components():
... print(quad)
SparseTriple(ind1 = [0], ind2 = [0], val = [1.0])
SparseTriple(ind1 = [0, 1], ind2 = [0, 1], val = [1.0, 2.0])
"""
def getquad(a):
return SparseTriple(*CPX_PROC.getqconstr_quad(
self._env._e, self._cplex._lp, a))
return apply_freeform_one_arg(
getquad, self._conv, self.get_num(), args)
[docs]
def get_names(self, *args):
"""Returns the names of a set of quadratic constraints.
Can be called by four forms.
quadratic_constraints.get_names()
return the names of all quadratic constraints from the
problem.
quadratic_constraints.get_names(i)
i must be a quadratic constraint index. Returns the name
of constraint i.
quadratic_constraints.get_names(s)
s must be a sequence of quadratic constraint indices.
Returns the names of the quadratic constraints with indices
the members of s. Equivalent to
[quadratic_constraints.get_names(i) for i in s]
quadratic_constraints.get_names(begin, end)
begin and end must be quadratic constraint indices. Returns
the names of the quadratic constraints with indices between
begin and end, inclusive of end. Equivalent to
quadratic_constraints.get_names(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(11)])
>>> [c.quadratic_constraints.add(
... name = "q" + str(i),
... quad_expr = [range(i), range(i), [1.0 * (j+1.0) for j in range(i)]])
... for i in range(1, 11)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.quadratic_constraints.get_num()
10
>>> c.quadratic_constraints.get_names(8)
'q9'
>>> c.quadratic_constraints.get_names(1, 3)
['q2', 'q3', 'q4']
>>> c.quadratic_constraints.get_names([2, 0, 5])
['q3', 'q1', 'q6']
>>> c.quadratic_constraints.get_names()
['q1', 'q2', 'q3', 'q4', 'q5', 'q6', 'q7', 'q8', 'q9', 'q10']
"""
def getname(a):
return CPX_PROC.getqconstrname(
self._env._e, self._cplex._lp, a)
return apply_freeform_one_arg(
getname, self._conv, self.get_num(), args)
[docs]
class SOSType(ConstantClass):
"""Constants defining the type of special ordered sets.
For a definition of SOS type 1 and 2, see those topics in the CPLEX
User's Manual.
"""
SOS1 = _constants.CPX_TYPE_SOS1
SOS2 = _constants.CPX_TYPE_SOS2
[docs]
class SOSInterface(BaseInterface):
"""Class containing methods for Special Ordered Sets (SOS)."""
type = SOSType()
"""See `SOSType()` """
[docs]
def __init__(self, cplex):
"""Creates a new SOSInterface.
The SOS interface is exposed by the top-level `Cplex` class as
`Cplex.SOS`. This constructor is not meant to be used
externally.
"""
super().__init__(cplex=cplex, getindexfunc=CPX_PROC.getsosindex)
[docs]
def get_num(self):
"""Returns the number of special ordered sets."""
return CPX_PROC.getnumsos(self._env._e, self._cplex._lp)
# FIXME: 'type' and 'SOS' are bad variable names. type is a "reserved"
# word and SOS should be lowercased.
def _add(self, type, SOS, name):
"""non-public"""
indices, weights = unpack_pair(SOS)
CPX_PROC.addsos(self._env._e, self._cplex._lp, type, [0],
self._cplex.variables._conv(indices),
weights, [name])
[docs]
def add(self, type="1", SOS=None, name=""):
"""Adds a special ordered set constraint to the problem.
Takes three keyword arguments.
type : can be either SOS.type.SOS1 or SOS.type.SOS2
SOS : either a SparsePair or a list of two lists, the first of
which contains variable indices or names, the second of which
contains the weights to assign to those variables.
name: the name of the SOS
Returns the index of the added SOS constraint.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(10)])
>>> c.SOS.add(type = "1", name = "type_one",
... SOS = cplex.SparsePair(ind = ["2", "3"],
... val = [25.0, 18.0]))
0
>>> c.SOS.add(type = "2", name = "type_two",
... SOS = cplex.SparsePair(ind = ["2", "4", "7", "3"],
... val = [1.0, 3.0, 25.0, 18.0]))
1
"""
if SOS is None:
SOS = SparsePair([0], [0.0])
# We only ever create one sos constraint at a time.
return self._add_single(self.get_num, self._add,
type, SOS, name)
[docs]
def delete(self, *args):
"""Deletes special ordered sets from the problem.
There are four forms by which SOS.delete may be called.
SOS.delete()
deletes all SOS constraints from the problem.
SOS.delete(i)
i must be a SOS constraint name or index. Deletes the SOS
constraint indexed as i or named i.
SOS.delete(s)
s must be a sequence of SOS constraint names or indices.
Deletes the SOS constraints with names or indices contained
within s. Equivalent to [SOS.delete(i) for i in s].
SOS.delete(begin, end)
begin and end must be SOS constraint indices or SOS constraint
names. Deletes the SOS constraints with indices between begin
and end, inclusive of end. Equivalent to
SOS.delete(range(begin, end + 1)). This will give the best
performance when deleting batches of SOS constraints.
See `CPXdelsos <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/mipapi/delsos.html>`_ in the Callable Library Reference Manual
for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=['x', 'y'])
>>> l = cplex.SparsePair(ind=['x'], val=[1.0])
>>> [c.SOS.add(name=str(i), SOS=l) for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.SOS.get_num()
10
>>> c.SOS.delete(8)
>>> c.SOS.get_names()
['0', '1', '2', '3', '4', '5', '6', '7', '9']
>>> c.SOS.delete("1", 3)
>>> c.SOS.get_names()
['0', '4', '5', '6', '7', '9']
>>> c.SOS.delete([2, "0", 5])
>>> c.SOS.get_names()
['4', '6', '7']
>>> c.SOS.delete()
>>> c.SOS.get_names()
[]
"""
def _delete(begin, end=None):
CPX_PROC.delsos(self._env._e, self._cplex._lp, begin, end)
delete_set_by_range(_delete, self._conv, self.get_num(), *args)
[docs]
def get_sets(self, *args):
"""Returns the sets of variables and their corresponding weights.
Returns a SparsePair instance or a list of SparsePair instances.
Can be called by four forms.
SOS.get_sets()
return the set of variables and weights of all SOS
constraints from the problem.
SOS.get_sets(i)
i must be a SOS constraint name or index. Returns the set
of variables and weights of the SOS constraint whose index
or name is i.
SOS.get_sets(s)
s must be a sequence of SOS constraint names or indices.
Returns the variables and weights of the SOS constraints
with indices the members of s. Equivalent to
[SOS.get_sets(i) for i in s]
SOS.get_sets(begin, end)
begin and end must be SOS constraint indices or SOS constraint
names. Returns the variables and weights of the SOS constraints
with indices between begin and end, inclusive of end.
Equivalent to SOS.get_sets(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names=[str(i) for i in range(4)],
... types="B" * 4
... )
>>> [c.SOS.add(
... name="s{0}".format(i),
... SOS=[range(i), [1.0 * (j+1.0) for j in range(i)]])
... for i in range(1, 3)]
[0, 1]
>>> c.SOS.get_num()
2
>>> c.SOS.get_sets(1)
SparsePair(ind = [0, 1], val = [1.0, 2.0])
>>> for s in c.SOS.get_sets("s1", 1):
... print(s)
SparsePair(ind = [0], val = [1.0])
SparsePair(ind = [0, 1], val = [1.0, 2.0])
>>> for s in c.SOS.get_sets(["s2", 0]):
... print(s)
SparsePair(ind = [0, 1], val = [1.0, 2.0])
SparsePair(ind = [0], val = [1.0])
>>> for s in c.SOS.get_sets():
... print(s)
SparsePair(ind = [0], val = [1.0])
SparsePair(ind = [0, 1], val = [1.0, 2.0])
"""
def getsos(a, b=self.get_num() - 1):
ret = CPX_PROC.getsos(self._env._e, self._cplex._lp, a, b)
mat = _HBMatrix()
mat.matbeg = ret[0]
mat.matind = ret[1]
mat.matval = ret[2]
return [m for m in mat]
return apply_freeform_two_args(getsos, self._conv, args)
[docs]
def get_types(self, *args):
"""Returns the type of a set of special ordered sets.
Return values are attributes of Cplex.SOS.type.
Can be called by four forms.
SOS.get_types()
return the type of all SOS constraints.
SOS.get_types(i)
i must be a SOS constraint name or index. Returns the type
of the SOS constraint whose index or name is i.
SOS.get_types(s)
s must be a sequence of SOS constraint names or indices.
Returns the type of the SOS constraints with indices the
members of s. Equivalent to [SOS.get_types(i) for i in s]
SOS.get_types(begin, end)
begin and end must be SOS constraint indices or SOS constraint
names. Returns the type of the SOS constraints with indices
between begin and end, inclusive of end. Equivalent to
SOS.get_types(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(11)], types = "B" * 11)
>>> [c.SOS.add(name = str(i), type = str(i % 2 + 1))
... for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.SOS.get_num()
10
>>> c.SOS.get_types(8)
'1'
>>> c.SOS.get_types("1",3)
['2', '1', '2']
>>> c.SOS.get_types([2,"0",5])
['1', '1', '2']
>>> c.SOS.get_types()
['1', '2', '1', '2', '1', '2', '1', '2', '1', '2']
"""
def gettype(a, b=self.get_num() - 1):
return CPX_PROC.getsos_info(self._env._e, self._cplex._lp, a, b)[0]
t = [i for i in "".join(apply_freeform_two_args(
gettype, self._conv, args))]
return t[0] if len(t) == 1 else t
[docs]
def get_num_members(self, *args):
"""Returns the size of a set of special ordered sets.
Can be called by four forms.
SOS.get_num_members()
return the number of variables in all SOS constraints from
the problem.
SOS.get_num_members(i)
i must be a SOS constraint name or index. Returns the
number of variables in the SOS constraint whose index or
name is i.
SOS.get_num_members(s)
s must be a sequence of SOS constraint names or indices.
Returns the number of variables in the SOS constraints with
indices the members of s. Equivalent to
[SOS.get_num_members(i) for i in s]
SOS.get_num_members(begin, end)
begin and end must be SOS constraint indices or SOS constraint
names. Returns the number of variables in the SOS constraints
with indices between begin and end, inclusive of end.
Equivalent to SOS.get_num_members(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(11)], types = "B" * 11)
>>> [c.SOS.add(name = str(i),
... SOS = [range(i), [1.0 * (j+1.0) for j in range(i)]])
... for i in range(1,11)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.SOS.get_num()
10
>>> c.SOS.get_num_members(7)
8
>>> c.SOS.get_num_members("1",2)
[1, 2, 3]
>>> c.SOS.get_num_members([3,"1",4])
[4, 1, 5]
>>> c.SOS.get_num_members()
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
"""
def getsize(a):
return CPX_PROC.getsos_info(self._env._e, self._cplex._lp, a, a)[1]
return apply_freeform_one_arg(
getsize, self._conv, self.get_num(), args)
[docs]
def get_names(self, *args):
"""Returns the names of a set of special ordered sets.
Can be called by four forms.
SOS.get_names()
return the names of all SOS constraints from the problem.
SOS.get_names(i)
i must be an SOS constraint index. Returns the name of
SOS constraint i.
SOS.get_names(s)
s must be a sequence of SOS constraint indices. Returns
the names of the SOS constraints with indices the members
of s. Equivalent to [SOS.get_names(i) for i in s]
SOS.get_names(begin, end)
begin and end must be SOS constraint indices. Returns the names
of the SOS constraints with indices between begin and end,
inclusive of end. Equivalent to
SOS.get_names(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ["x0"])
>>> [c.SOS.add(name = "sos" + str(i)) for i in range(1, 11)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.SOS.get_num()
10
>>> c.SOS.get_names(8)
'sos9'
>>> c.SOS.get_names(1, 3)
['sos2', 'sos3', 'sos4']
>>> c.SOS.get_names([2, 0, 5])
['sos3', 'sos1', 'sos6']
>>> c.SOS.get_names()
['sos1', 'sos2', 'sos3', 'sos4', 'sos5', 'sos6', 'sos7', 'sos8', 'sos9', 'sos10']
"""
def getname(a, b=self.get_num() - 1):
return CPX_PROC.getsosname(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(getname, self._conv, args)
[docs]
class EffortLevel(ConstantClass):
"""Effort levels associated with a MIP start"""
auto = _constants.CPX_MIPSTART_AUTO
check_feasibility = _constants.CPX_MIPSTART_CHECKFEAS
solve_fixed = _constants.CPX_MIPSTART_SOLVEFIXED
solve_MIP = _constants.CPX_MIPSTART_SOLVEMIP
repair = _constants.CPX_MIPSTART_REPAIR
no_check = _constants.CPX_MIPSTART_NOCHECK
[docs]
class MIPStartsInterface(BaseInterface):
"""Contains methods pertaining to MIP starts."""
effort_level = EffortLevel()
"""See `EffortLevel()` """
[docs]
def __init__(self, cplex):
"""Creates a new MIPStartsInterface.
The MIP starts interface is exposed by the top-level `Cplex`
class as `Cplex.MIP_starts`. This constructor is not meant to be
used externally.
"""
super().__init__(cplex=cplex,
getindexfunc=CPX_PROC.getmipstartindex)
[docs]
def get_num(self):
"""Returns the number of MIP starts currently stored.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names = [str(i) for i in range(11)],
... types = "I" * 11)
>>> indices = c.MIP_starts.add(
... [(cplex.SparsePair(ind = [i], val = [0.0]),
... c.MIP_starts.effort_level.auto) for i in range(5)])
>>> c.MIP_starts.get_num()
5
"""
return CPX_PROC.getnummipstarts(self._env._e, self._cplex._lp)
[docs]
def read(self, filename):
"""Reads MIP starts from a file.
This method reads a file in the format MST and copies the
information of all the MIP starts contained in that file into a
CPLEX problem object. The parameter cplex.parameters.advance
must be set to cplex.parameters.advance.values.standard, its
default value, or cplex.parameters.advance.values.alternate
in order for the MIP starts to be used.
Note
If the MIP start file is successfully read, then any
previously existing MIP starts will be deleted.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> c.MIP_starts.write("test_all.mst")
>>> c.MIP_starts.read("test_all.mst")
"""
CPX_PROC.readcopymipstarts(self._env._e, self._cplex._lp, filename)
[docs]
def write(self, filename, begin=-1, end=-1):
"""Writes a set of MIP starts to a file.
If called with only a filename, writes all MIP starts to that
file.
If called with a filename and one index or name of a MIP
start, writes only that MIP start to the file.
If called with a filename and two indices or names of MIP
starts, writes all MIP starts between the first and second
index or name, inclusive of begin and end, to the file.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names = [str(i) for i in range(11)], types = "I" * 11)
>>> indices = c.MIP_starts.add(
... [(cplex.SparsePair(ind = [i], val = [0.0]),
... c.MIP_starts.effort_level.auto) for i in range(5)])
>>> c.MIP_starts.write("test_all.mst")
>>> c.MIP_starts.write("test_one.mst", 1)
>>> c.MIP_starts.write("test_four.mst", 1, 4)
"""
if begin == -1 and end == -1:
begin = 0
end = self.get_num() - 1
if end == -1:
end = begin
CPX_PROC.writemipstarts(self._env._e, self._cplex._lp, filename,
begin, end)
def _add(self, *args):
"""non-public"""
if len(args) == 1:
for arg in args[0]:
self._add(*arg)
else:
if len(args) == 2:
name = ""
elif len(args) == 3:
name = args[2]
else:
raise WrongNumberOfArgumentsError()
ind, val = unpack_pair(args[0])
CPX_PROC.addmipstarts(
self._env._e, self._cplex._lp, [0],
self._cplex.variables._conv(ind),
val, [args[1]], [name])
[docs]
def add(self, *args):
"""Adds MIP starts to the problem.
To add a single MIP start, call this method as
cpx.MIP_starts.add(start, effort_level, name)
The first argument, start, must be either a SparsePair
instance or a list of two lists, the first of which contains
variable indices or names, the second of which contains the
values that those variables take.
The second argument, effort_level, must be an attribute of
MIP_starts.effort_level.
The third optional argument is the name of the MIP start.
To add a set of MIP starts, call this method as
cpx.MIP_starts.add(sequence)
where sequence is a list or tuple of pairs (start,
effort_level) or triples (start, effort_level, name) as
described above.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(11)],
... types = "I" * 11)
>>> indices = c.MIP_starts.add(
... cplex.SparsePair(ind = [0], val = [0.0]),
... c.MIP_starts.effort_level.repair, "first")
>>> indices = c.MIP_starts.add(
... cplex.SparsePair(ind = [1], val = [0.0]),
... c.MIP_starts.effort_level.solve_MIP)
>>> indices = c.MIP_starts.add(
... [([[2, 4], [0.0, 1.0]],
... c.MIP_starts.effort_level.auto, "third"),
... ([[3, 4], [1.0, 3.0]],
... c.MIP_starts.effort_level.check_feasibility)])
>>> c.MIP_starts.get_num()
4
>>> c.MIP_starts.get_names()
['first', 'm2', 'third', 'm4']
"""
return self._add_iter(self.get_num, self._add, *args)
[docs]
def change(self, *args):
"""Changes a MIP start or set of MIP starts.
To change a single MIP start, call this method as
cpx.MIP_starts.change(ID, start, effort_level)
The first argument, ID, must be an index or name of an
existing MIP start.
The second argument, start, must be either a SparsePair
instance or a list of two lists, the first of which contains
variable indices or names, the second of which contains the
values that those variables take. If the MIP start identified
by ID already has a value for a variable specified by start,
that value is replaced.
The third argument, effort_level, must be an attribute of
MIP_starts.effort_level.
To change multiple MIP starts, call this method as
cpx.MIP_starts.change(sequence)
where sequence is a list of tuple of triples (ID, start,
effort_level) as described above.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names = ["x{0}".format(i) for i in range(4)],
... types = "I" * 4
... )
>>> indices = c.MIP_starts.add(
... [(cplex.SparsePair(ind = [i], val = [0.0]),
... c.MIP_starts.effort_level.auto) for i in range(3)])
>>> for s in c.MIP_starts.get_starts():
... print(s)
(SparsePair(ind = [0], val = [0.0]), 0)
(SparsePair(ind = [1], val = [0.0]), 0)
(SparsePair(ind = [2], val = [0.0]), 0)
>>> c.MIP_starts.get_names()
['m1', 'm2', 'm3']
>>> check = c.MIP_starts.effort_level.check_feasibility
>>> repair = c.MIP_starts.effort_level.repair
>>> c.MIP_starts.change("m1", [["x0", "x1"], [1.0, 2.0]], check)
>>> c.MIP_starts.get_starts("m1")
(SparsePair(ind = [0, 1], val = [1.0, 2.0]), 1)
>>> c.MIP_starts.change(1, [[1, 2], [-1.0, -2.0]], repair)
>>> c.MIP_starts.get_starts("m2")
(SparsePair(ind = [1, 2], val = [-1.0, -2.0]), 4)
>>> c.MIP_starts.change([(1, [[0, 2], [-1.0, 2.0]], check),
... ("m3", [["x0", 2], [3.0, 2.0]], repair)])
>>> for s in c.MIP_starts.get_starts(["m2", "m3"]):
... print(s)
(SparsePair(ind = [0, 1, 2], val = [-1.0, -1.0, 2.0]), 1)
(SparsePair(ind = [0, 2], val = [3.0, 2.0]), 4)
"""
if len(args) == 3:
ind, val = unpack_pair(args[1])
CPX_PROC.chgmipstarts(
self._env._e, self._cplex._lp, [self._conv(args[0])], [0],
self._cplex.variables._conv(ind),
val, [args[2]])
elif len(args) == 1:
for arg in args[0]:
self.change(arg[0], arg[1], arg[2])
else:
raise WrongNumberOfArgumentsError()
[docs]
def delete(self, *args):
"""Deletes MIP starts from the problem.
There are four forms by which MIP_starts.delete may be called.
MIP_starts.delete()
deletes all MIP starts from the problem.
MIP_starts.delete(i)
i must be a MIP start name or index. Deletes the MIP start
whose index or name is i.
MIP_starts.delete(s)
s must be a sequence of MIP start names or indices.
Deletes the MIP starts with names or indices contained within
s. Equivalent to [MIP_starts.delete(i) for i in s].
MIP_starts.delete(begin, end)
begin and end must be MIP start indices or MIP start names.
Deletes the MIP starts with indices between begin and end,
inclusive of end. Equivalent to
MIP_starts.delete(range(begin, end + 1)). This will give the
best performance when deleting batches of MIP starts.
See `CPXdelmipstarts <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/mipapi/delmipstarts.html>`_ in the Callable Library Reference
Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=['x', 'y'], types=["II"])
>>> indices = c.MIP_starts.add(
... [(cplex.SparsePair(ind=['x'], val=[1.0]),
... c.MIP_starts.effort_level.auto, str(i))
... for i in range(10)])
>>> c.MIP_starts.get_num()
10
>>> c.MIP_starts.delete(8)
>>> c.MIP_starts.get_names()
['0', '1', '2', '3', '4', '5', '6', '7', '9']
>>> c.MIP_starts.delete("1", 3)
>>> c.MIP_starts.get_names()
['0', '4', '5', '6', '7', '9']
>>> c.MIP_starts.delete([2, "0", 5])
>>> c.MIP_starts.get_names()
['4', '6', '7']
>>> c.MIP_starts.delete()
>>> c.MIP_starts.get_names()
[]
"""
def _delete(begin, end=None):
CPX_PROC.delmipstarts(self._env._e, self._cplex._lp, begin, end)
delete_set_by_range(_delete, self._conv, self.get_num(), *args)
[docs]
def get_starts(self, *args):
"""Returns a set of MIP starts.
Returns a SparsePair instance or a list of SparsePair instances.
Can be called by four forms.
MIP_starts.get_starts()
return the starting vector for all MIP starts from the
problem.
MIP_starts.get_starts(i)
i must be a MIP start name or index. Returns the starting
vector for the MIP start whose index or name is i.
MIP_starts.get_starts(s)
s must be a sequence of MIP start names or indices.
Returns the starting vector for the MIP starts with indices
the members of s. Equivalent to [MIP_starts.get_starts(i)
for i in s]
MIP_starts.get_starts(begin, end)
begin and end must be MIP start indices or MIP start names.
Returns the starting vector for the MIP starts with indices
between begin and end, inclusive of end. Equivalent to
MIP_starts.get_starts(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names=[str(i) for i in range(11)],
... types="B" * 11)
>>> indices =c.MIP_starts.add(
... [(cplex.SparsePair(ind=[i], val=[1.0 * i]),
... c.MIP_starts.effort_level.auto, str(i))
... for i in range(10)])
>>> c.MIP_starts.get_num()
10
>>> c.MIP_starts.get_starts(7)
(SparsePair(ind = [7], val = [7.0]), 0)
>>> for s in c.MIP_starts.get_starts("0", 2):
... print(s)
(SparsePair(ind = [0], val = [0.0]), 0)
(SparsePair(ind = [1], val = [1.0]), 0)
(SparsePair(ind = [2], val = [2.0]), 0)
>>> for s in c.MIP_starts.get_starts([2, "0", 5]):
... print(s)
(SparsePair(ind = [2], val = [2.0]), 0)
(SparsePair(ind = [0], val = [0.0]), 0)
(SparsePair(ind = [5], val = [5.0]), 0)
>>> c.MIP_starts.delete(3,9)
>>> for s in c.MIP_starts.get_starts():
... print(s)
(SparsePair(ind = [0], val = [0.0]), 0)
(SparsePair(ind = [1], val = [1.0]), 0)
(SparsePair(ind = [2], val = [2.0]), 0)
>>> c.MIP_starts.effort_level[0]
'auto'
"""
def getmst(a, b=self.get_num() - 1):
ret = CPX_PROC.getmipstarts(self._env._e, self._cplex._lp, a, b)
mat = _HBMatrix()
mat.matbeg = ret[0]
mat.matind = ret[1]
mat.matval = ret[2]
return [(m, ret[3][i]) for (i, m) in enumerate(mat)]
return apply_freeform_two_args(getmst, self._conv, args)
[docs]
def get_effort_levels(self, *args):
"""Returns the effort levels for a set of MIP starts.
Can be called by four forms.
MIP_starts.get_effort_levels()
return the effort level for all MIP starts from the
problem.
MIP_starts.get_effort_levels(i)
i must be a MIP start name or index. Returns the effort
level for the MIP start whose index or name is i.
MIP_starts.get_effort_levels(s)
s must be a sequence of MIP start names or indices.
Returns the effort level for the MIP starts with indices
the members of s. Equivalent to
[MIP_starts.get_effort_levels(i) for i in s]
MIP_starts.get_effort_levels(begin, end)
begin and end must be MIP start indices or MIP start names.
Returns the effort level for the MIP starts with indices
between begin and end, inclusive of end. Equivalent to
MIP_starts.get_effort_levels(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names = [str(i) for i in range(10)],
... types = "B" * 10)
>>> indices = c.MIP_starts.add(
... [(cplex.SparsePair(ind = [i], val = [1.0 * i]),
... c.MIP_starts.effort_level.auto, str(i))
... for i in range(10)])
>>> c.MIP_starts.change([(1, [[0], [0.0]], c.MIP_starts.effort_level.check_feasibility),
(2, [[0], [0.0]], c.MIP_starts.effort_level.solve_fixed),
(3, [[0], [0.0]], c.MIP_starts.effort_level.solve_MIP),
(4, [[0], [0.0]], c.MIP_starts.effort_level.repair),
(5, [[0], [0.0]], c.MIP_starts.effort_level.no_check)])
>>> c.MIP_starts.get_num()
10
>>> c.MIP_starts.effort_level[c.MIP_starts.get_effort_levels(3)]
'solve_MIP'
>>> [c.MIP_starts.effort_level[i] for i in c.MIP_starts.get_effort_levels("0",2)]
['auto', 'check_feasibility', 'solve_fixed']
>>> [c.MIP_starts.effort_level[i] for i in c.MIP_starts.get_effort_levels([2,"0",5])]
['solve_fixed', 'auto', 'no_check']
>>> c.MIP_starts.get_effort_levels()
[0, 1, 2, 3, 4, 5, 0, 0, 0, 0]
"""
def geteffort(a, b=self.get_num() - 1):
return CPX_PROC.getmipstarts_effort(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(geteffort, self._conv, args)
[docs]
def get_num_entries(self, *args):
"""Returns the number of variables specified by a set of MIP starts.
Can be called by four forms.
MIP_starts.get_num_entries()
return the length of the starting vector for all MIP starts
from the problem.
MIP_starts.get_num_entries(i)
i must be a MIP start name or index. Returns the length of
the starting vector for the MIP start whose index or name
is i.
MIP_starts.get_num_entries(s)
s must be a sequence of MIP start names or indices.
Returns the length of the starting vector for the MIP
starts with indices the members of s. Equivalent to
[MIP_starts.get_num_entries(i) for i in s]
MIP_starts.get_num_entries(begin, end)
begin and end must be MIP start indices or MIP start names.
Returns the length of the starting vector for the MIP starts
with indices between begin and end, inclusive of end.
Equivalent to
MIP_starts.get_num_entries(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names = [str(i) for i in range(11)],
... types = "B" * 11)
>>> indices = c.MIP_starts.add(
... [(cplex.SparsePair(ind = range(i), val = [0.0] * i),
... c.MIP_starts.effort_level.auto, str(i - 1))
... for i in range(1, 11)])
>>> c.MIP_starts.get_num()
10
>>> c.MIP_starts.get_num_entries(3)
4
>>> c.MIP_starts.get_num_entries("0",2)
[1, 2, 3]
>>> c.MIP_starts.get_num_entries([2,"0",5])
[3, 1, 6]
>>> c.MIP_starts.get_num_entries()
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
"""
def getmstsize(a):
return CPX_PROC.getmipstarts_size(self._env._e, self._cplex._lp, a, a)
return apply_freeform_one_arg(
getmstsize, self._conv, self.get_num(), args)
[docs]
def get_names(self, *args):
"""Returns the names of a set of MIP starts.
Can be called by four forms.
MIP_starts.get_names()
return the names of all MIP starts from the problem.
MIP_starts.get_names(i)
i must be a MIP start index. Returns the name of MIP start i.
MIP_starts.get_names(s)
s must be a sequence of MIP start indices. Returns the
names of the MIP starts with indices the members of s.
Equivalent to [MIP_starts.get_names(i) for i in s]
MIP_starts.get_names(begin, end)
begin and end must be MIP start indices. Returns the names of
the MIP starts with indices between begin and end, inclusive of
end. Equivalent to MIP_starts.get_names(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names = [str(i) for i in range(11)],
... types = "B" * 11)
>>> indices = c.MIP_starts.add(
... [(cplex.SparsePair(ind = range(i), val = [0.0] * i),
... c.MIP_starts.effort_level.auto, "mst" + str(i - 1))
... for i in range(1, 11)])
>>> c.MIP_starts.get_num()
10
>>> c.MIP_starts.get_names(8)
'mst8'
>>> c.MIP_starts.get_names(1, 3)
['mst1', 'mst2', 'mst3']
>>> c.MIP_starts.get_names([2, 0, 5])
['mst2', 'mst0', 'mst5']
>>> c.MIP_starts.get_names()
['mst0', 'mst1', 'mst2', 'mst3', 'mst4', 'mst5', 'mst6', 'mst7', 'mst8', 'mst9']
"""
def getname(a, b=self.get_num() - 1):
return CPX_PROC.getmipstartname(self._env._e, self._cplex._lp,
a, b)
return apply_freeform_two_args(getname, self._conv, args)
[docs]
class ObjSense(ConstantClass):
"""Constants defining the sense of the objective function.
See `CPXgetobjsen <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/getobjsen.html>`_ in the Callable Library Reference
Manual for more detail.
"""
maximize = _constants.CPX_MAX
"""See CPX_MAX in the C API."""
minimize = _constants.CPX_MIN
"""See CPX_MIN in the C API."""
[docs]
class ObjectiveInterface(BaseInterface):
"""Contains methods for querying and modifying the objective function."""
sense = ObjSense()
"""See `ObjSense()` """
[docs]
def set_linear(self, *args):
"""Changes the linear part of the objective function.
Can be called by two forms:
objective.set_linear(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.
objective.set_linear(sequence)
sequence is a sequence of pairs (var, value) as described
above. Changes the coefficients for the specified
variables to the given values.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(4)])
>>> c.objective.get_linear()
[0.0, 0.0, 0.0, 0.0]
>>> c.objective.set_linear(0, 1.0)
>>> c.objective.get_linear()
[1.0, 0.0, 0.0, 0.0]
>>> c.objective.set_linear("3", -1.0)
>>> c.objective.get_linear()
[1.0, 0.0, 0.0, -1.0]
>>> c.objective.set_linear([("2", 2.0), (1, 0.5)])
>>> c.objective.get_linear()
[1.0, 0.5, 2.0, -1.0]
"""
def chgobj(a, b):
CPX_PROC.chgobj(self._env._e, self._cplex._lp, a, b)
apply_pairs(chgobj, self._cplex.variables._conv, *args)
[docs]
def set_quadratic(self, *args):
"""Sets the quadratic part of the objective function.
Call this method with a list with length equal to the number
of variables in the problem.
If the quadratic objective function is separable, the entries
of the list must all be of type float or int.
If the quadratic objective function is not separable, the
entries of the list must be either SparsePair instances or
lists of two lists, the first of which contains variable
indices or names, the second of which contains the values that
those variables take.
Note
Successive calls to set_quadratic will overwrite any previous
quadratic objective function. To modify only part of the
quadratic objective function, use the method
set_quadratic_coefficients.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=[str(i) for i in range(3)])
>>> c.objective.set_quadratic(
... [cplex.SparsePair(ind=[0, 1, 2], val=[1.0, -2.0, 0.5]),
... cplex.SparsePair(ind=[0, 1], val=[-2.0, -1.0]),
... cplex.SparsePair(ind=[0, 2], val=[0.5, -3.0])]
... )
>>> for q in c.objective.get_quadratic():
... print(q)
SparsePair(ind = [0, 1, 2], val = [1.0, -2.0, 0.5])
SparsePair(ind = [0, 1], val = [-2.0, -1.0])
SparsePair(ind = [0, 2], val = [0.5, -3.0])
>>> c.objective.set_quadratic([1.0, 2.0, 3.0])
>>> for q in c.objective.get_quadratic():
... print(q)
SparsePair(ind = [0], val = [1.0])
SparsePair(ind = [1], val = [2.0])
SparsePair(ind = [2], val = [3.0])
"""
if len(args) != 1:
raise WrongNumberOfArgumentsError()
if isinstance(args[0], _HBMatrix):
CPX_PROC.copyquad(
self._env._e, self._cplex._lp, args[0].matbeg,
self._cplex.variables._conv(args[0].matind),
args[0].matval)
elif isinstance(args[0][0], numbers.Number):
CPX_PROC.copyqpsep(self._env._e, self._cplex._lp, args[0])
else:
self.set_quadratic(_HBMatrix(args[0]))
[docs]
def set_quadratic_coefficients(self, *args):
"""Sets coefficients of the quadratic component of the objective
function.
To set a single coefficient, call this method as
objective.set_quadratic_coefficients(v1, v2, val)
where v1 and v2 are names or indices of variables and val is
the value for the coefficient.
To set multiple coefficients, call this method as
objective.set_quadratic_coefficients(sequence)
where sequence is a list or tuple of triples (v1, v2, val) as
described above.
Note
Since the quadratic objective function must be symmetric, each
triple in which v1 is different from v2 is used to set both
the (v1, v2) coefficient and the (v2, v1) coefficient. If
(v1, v2) and (v2, v1) are set with a single call, the second
value is stored.
Note
Attempting to set many coefficients with set_quadratic_coefficients
can be time consuming. Instead, use the method set_quadratic to set
the quadratic part of the objective efficiently.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=[str(i) for i in range(3)])
>>> c.objective.set_quadratic_coefficients(0, 1, 1.0)
>>> for q in c.objective.get_quadratic():
... print(q)
SparsePair(ind = [1], val = [1.0])
SparsePair(ind = [0], val = [1.0])
SparsePair(ind = [], val = [])
>>> c.objective.set_quadratic_coefficients([(1, 1, 2.0),
... (0, 2, 3.0)])
>>> for q in c.objective.get_quadratic():
... print(q)
SparsePair(ind = [1, 2], val = [1.0, 3.0])
SparsePair(ind = [0, 1], val = [1.0, 2.0])
SparsePair(ind = [0], val = [3.0])
>>> c.objective.set_quadratic_coefficients([(0, 1, 4.0),
... (1, 0, 5.0)])
>>> for q in c.objective.get_quadratic():
... print(q)
SparsePair(ind = [1, 2], val = [5.0, 3.0])
SparsePair(ind = [0, 1], val = [5.0, 2.0])
SparsePair(ind = [0], val = [3.0])
"""
if len(args) not in (3, 1):
raise WrongNumberOfArgumentsError()
if isinstance(args[0], (str, int)):
arg_list = [args]
else:
arg_list = args[0]
varcache = {}
for i, j, val in arg_list:
CPX_PROC.chgqpcoef(self._env._e, self._cplex._lp,
self._cplex.variables._conv(i, varcache),
self._cplex.variables._conv(j, varcache),
val)
[docs]
def set_sense(self, sense):
"""Sets the sense of the objective function.
The argument to this method must be either
objective.sense.minimize or objective.sense.maximize.
>>> import cplex
>>> c = cplex.Cplex()
>>> c.objective.sense[c.objective.get_sense()]
'minimize'
>>> c.objective.set_sense(c.objective.sense.maximize)
>>> c.objective.sense[c.objective.get_sense()]
'maximize'
>>> c.objective.set_sense(c.objective.sense.minimize)
>>> c.objective.sense[c.objective.get_sense()]
'minimize'
"""
CPX_PROC.chgobjsen(self._env._e, self._cplex._lp, sense)
[docs]
def set_name(self, name):
"""Sets the name of the objective function.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.objective.set_name("cost")
>>> c.objective.get_name()
'cost'
"""
CPX_PROC.copyobjname(self._env._e, self._cplex._lp, name)
[docs]
def get_linear(self, *args):
"""Returns the linear coefficients of a set of variables.
Can be called by four forms.
objective.get_linear()
return the linear objective coefficients of all variables
from the problem.
objective.get_linear(i)
i must be a variable name or index. Returns the linear
objective coefficient of the variable whose index or name
is i.
objective.get_linear(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
[objective.get_linear(i) for i in s]
objective.get_linear(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
objective.get_linear(range(begin, end + 1)).
>>> 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.objective.get_linear(8)
12.0
>>> c.objective.get_linear("1",3)
[1.5, 3.0, 4.5]
>>> c.objective.get_linear([2,"0",5])
[3.0, 0.0, 7.5]
>>> c.objective.get_linear()
[0.0, 1.5, 3.0, 4.5, 6.0, 7.5, 9.0, 10.5, 12.0, 13.5]
"""
def getobj(a, b=self._cplex.variables.get_num() - 1):
return CPX_PROC.getobj(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(
getobj, self._cplex.variables._conv, args)
[docs]
def get_quadratic(self, *args):
"""Returns a set of columns of the quadratic component of the
objective function.
Returns a SparsePair instance or a list of SparsePair instances.
Can be called by four forms.
objective.get_quadratic()
return the entire quadratic objective function.
objective.get_quadratic(i)
i must be a variable name or index. Returns the column of
the quadratic objective function associated with the
variable whose index or name is i.
objective.get_quadratic(s)
s must be a sequence of variable names or indices. Returns
the columns of the quadratic objective function associated
with the variables with indices the members of s.
Equivalent to [objective.get_quadratic(i) for i in s]
objective.get_quadratic(begin, end)
begin and end must be variable indices or variable names.
Returns the columns of the quadratic objective function
associated with the variables with indices between begin and
end, inclusive of end. Equivalent to
objective.get_quadratic(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=[str(i) for i in range(10)])
>>> c.variables.get_num()
10
>>> c.objective.set_quadratic([1.5 * i for i in range(10)])
>>> c.objective.get_quadratic(8)
SparsePair(ind = [8], val = [12.0])
>>> for q in c.objective.get_quadratic("1", 3):
... print(q)
SparsePair(ind = [1], val = [1.5])
SparsePair(ind = [2], val = [3.0])
SparsePair(ind = [3], val = [4.5])
>>> for q in c.objective.get_quadratic([3, "1", 5]):
... print(q)
SparsePair(ind = [3], val = [4.5])
SparsePair(ind = [1], val = [1.5])
SparsePair(ind = [5], val = [7.5])
>>> for q in c.objective.get_quadratic():
... print(q)
SparsePair(ind = [], val = [])
SparsePair(ind = [1], val = [1.5])
SparsePair(ind = [2], val = [3.0])
SparsePair(ind = [3], val = [4.5])
SparsePair(ind = [4], val = [6.0])
SparsePair(ind = [5], val = [7.5])
SparsePair(ind = [6], val = [9.0])
SparsePair(ind = [7], val = [10.5])
SparsePair(ind = [8], val = [12.0])
SparsePair(ind = [9], val = [13.5])
"""
num = self._cplex.variables.get_num()
def getquad(begin, end=num - 1):
mat = _HBMatrix()
t = CPX_PROC.getquad(self._env._e, self._cplex._lp, begin, end)
mat.matbeg, mat.matind, mat.matval = t
return [m for m in mat]
return apply_freeform_two_args(
getquad, self._cplex.variables._conv, args)
[docs]
def get_quadratic_coefficients(self, *args):
"""Returns individual coefficients from the quadratic objective function.
To query a single coefficient, call this as
objective.get_quadratic_coefficients(v1, v2)
where v1 and v2 are indices or names of variables.
To query multiple coefficients, call this method as
objective.get_quadratic_coefficients(sequence)
where sequence is a list or tuple of pairs (v1, v2) as
described above.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(3)])
>>> c.objective.set_quadratic_coefficients(0, 1, 1.0)
>>> c.objective.get_quadratic_coefficients("1", 0)
1.0
>>> c.objective.set_quadratic_coefficients([(1, 1, 2.0), (0, 2, 3.0), (1, 0, 5.0)])
>>> c.objective.get_quadratic_coefficients([(1, 0), (1, "1"), (2, "0")])
[5.0, 2.0, 3.0]
"""
def getqpcoef(v1, v2):
return CPX_PROC.getqpcoef(self._env._e, self._cplex._lp, v1, v2)
if len(args) == 2:
indices = self._cplex.variables._conv(args)
return getqpcoef(indices[0], indices[1])
if len(args) == 1:
return [self.get_quadratic_coefficients(*arg) for arg in args[0]]
raise WrongNumberOfArgumentsError()
[docs]
def get_sense(self):
"""Returns the sense of the objective function.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.objective.sense[c.objective.get_sense()]
'minimize'
>>> c.objective.set_sense(c.objective.sense.maximize)
>>> c.objective.sense[c.objective.get_sense()]
'maximize'
>>> c.objective.set_sense(c.objective.sense.minimize)
>>> c.objective.sense[c.objective.get_sense()]
'minimize'
"""
return CPX_PROC.getobjsen(self._env._e, self._cplex._lp)
[docs]
def get_name(self):
"""Returns the name of the objective function.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.objective.set_name("cost")
>>> c.objective.get_name()
'cost'
"""
return CPX_PROC.getobjname(self._env._e, self._cplex._lp)
[docs]
def get_num_quadratic_variables(self):
"""Returns the number of variables with quadratic coefficients.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(3)])
>>> c.objective.set_quadratic_coefficients(0, 1, 1.0)
>>> c.objective.get_num_quadratic_variables()
2
>>> c.objective.set_quadratic([1.0, 0.0, 0.0])
>>> c.objective.get_num_quadratic_variables()
1
>>> c.objective.set_quadratic_coefficients([(1, 1, 2.0), (0, 2, 3.0)])
>>> c.objective.get_num_quadratic_variables()
3
"""
return CPX_PROC.getnumquad(self._env._e, self._cplex._lp)
[docs]
def get_num_quadratic_nonzeros(self):
"""Returns the number of nonzeros in the quadratic objective function.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(3)])
>>> c.objective.set_quadratic_coefficients(0, 1, 1.0)
>>> c.objective.get_num_quadratic_nonzeros()
2
>>> c.objective.set_quadratic_coefficients([(1, 1, 2.0), (0, 2, 3.0)])
>>> c.objective.get_num_quadratic_nonzeros()
5
>>> c.objective.set_quadratic_coefficients([(0, 1, 4.0), (1, 0, 0.0)])
>>> c.objective.get_num_quadratic_nonzeros()
3
"""
return CPX_PROC.getnumqpnz(self._env._e, self._cplex._lp)
[docs]
def get_offset(self):
"""Returns the constant offset of the objective function for a problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> offset = c.objective.get_offset()
>>> abs(offset - 0.0) < 1e-6
True
"""
return CPX_PROC.getobjoffset(self._env._e, self._cplex._lp)
[docs]
def set_offset(self, offset):
"""Sets the constant offset of the objective function for a problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.objective.set_offset(3.14)
>>> offset = c.objective.get_offset()
>>> abs(offset - 3.14) < 1e-6
True
"""
return CPX_PROC.chgobjoffset(self._env._e, self._cplex._lp, offset)
[docs]
class ProgressInterface(BaseInterface):
"""Methods to query the progress of optimization."""
[docs]
def __init__(self, parent):
"""Creates a new ProgressInterface.
The progress interface is exposed by the top-level `Cplex`
class as Cplex.solution.progress. This constructor is not
meant to be used externally.
"""
super().__init__(cplex=parent._cplex, advanced=True)
[docs]
def get_num_iterations(self):
"""Returns the number of iterations executed so far.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> num_iter = c.solution.progress.get_num_iterations()
"""
if self._cplex._is_MIP():
return CPX_PROC.getmipitcnt(self._env._e, self._cplex._lp)
siftcnt = CPX_PROC.getsiftitcnt(self._env._e, self._cplex._lp)
if siftcnt > 0:
return siftcnt
baritcnt = CPX_PROC.getbaritcnt(self._env._e, self._cplex._lp)
if baritcnt > 0:
return baritcnt
return CPX_PROC.getitcnt(self._env._e, self._cplex._lp)
[docs]
def get_num_barrier_iterations(self):
"""Returns the number of barrier iterations.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("qcp.lp")
>>> c.solve()
>>> num_iter = c.solution.progress.get_num_barrier_iterations()
"""
return CPX_PROC.getbaritcnt(self._env._e, self._cplex._lp)
[docs]
def get_num_sifting_iterations(self):
"""Returns the number of sifting iterations.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.sifting)
>>> c.solve()
>>> num_iter = c.solution.progress.get_num_sifting_iterations()
"""
return CPX_PROC.getsiftitcnt(self._env._e, self._cplex._lp)
[docs]
def get_num_phase_one_iterations(self):
"""Returns the number of iterations to find a feasible solution.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> num_iter = c.solution.progress.get_num_phase_one_iterations()
"""
return CPX_PROC.getphase1cnt(self._env._e, self._cplex._lp)
[docs]
def get_num_sifting_phase_one_iterations(self):
"""Returns the number of sifting iterations to find a feasible solution.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.sifting)
>>> c.solve()
>>> num_iter = c.solution.progress.get_num_sifting_phase_one_iterations()
"""
return CPX_PROC.getsiftphase1cnt(self._env._e, self._cplex._lp)
[docs]
def get_num_nodes_processed(self):
"""Returns the number of nodes processed.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.parameters.randomseed.set(1)
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> num_nodes = c.solution.progress.get_num_nodes_processed()
"""
return CPX_PROC.getnodecnt(self._env._e, self._cplex._lp)
[docs]
def get_num_nodes_remaining(self):
"""Returns the number of nodes left to process.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> num_nodes = c.solution.progress.get_num_nodes_remaining()
"""
return CPX_PROC.getnodeleftcnt(self._env._e, self._cplex._lp)
[docs]
def get_num_primal_push(self):
"""Returns the number of primal push operations.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.barrier)
>>> c.solve()
>>> num_push = c.solution.progress.get_num_primal_push()
"""
return CPX_PROC.getcrossppushcnt(self._env._e, self._cplex._lp)
[docs]
def get_num_primal_exchange(self):
"""Returns the number of primal exchange operations.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.barrier)
>>> c.solve()
>>> num_exch = c.solution.progress.get_num_primal_exchange()
"""
return CPX_PROC.getcrosspexchcnt(self._env._e, self._cplex._lp)
[docs]
def get_num_dual_push(self):
"""Returns the number of dual push operations.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.barrier)
>>> c.solve()
>>> num_push = c.solution.progress.get_num_dual_push()
"""
return CPX_PROC.getcrossdpushcnt(self._env._e, self._cplex._lp)
[docs]
def get_num_dual_exchange(self):
"""Returns the number of dual exchange operations.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.barrier)
>>> c.solve()
>>> num_exch = c.solution.progress.get_num_dual_exchange()
"""
return CPX_PROC.getcrossdexchcnt(self._env._e, self._cplex._lp)
[docs]
def get_num_conflict_passes(self):
"""Returns the number of passes performed by the conflict
refiner.
See `CPXgetconflictnumpasses <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/getconflictnumpasses.html>`_ in the Callable Library
Reference Manual.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> c.read("infeasible.lp")
>>> c.conflict.refine()
>>> c.solution.progress.get_num_conflict_passes()
5
"""
return CPX_PROC.getconflictnumpasses(self._env._e, self._cplex._lp)
[docs]
class InfeasibilityInterface(BaseInterface):
"""Methods for computing degree of infeasibility in a solution vector.
Each of these methods takes one required argument, x, which must
be a list of floats with length equal to the number of variables.
If no other arguments are provided, the methods return the
violation for all constraints of the given type.
If one string or integer is provided, it is taken to be the name
or index of a constraint of the given type. The methods return
the violation of that constraint.
If two strings or integers are provided, they are taken to be the
names or indices of constraints of the given type. All violations
for constraints between the first and second, inclusive, are
returned in a list.
If a sequence of strings or integers are provided, they are taken
to be the names or indices of constraints of the given type. All
violations for constraints identified in the sequence are returned
in a list.
"""
[docs]
def __init__(self, parent):
"""Creates a new InfeasibilityInterface.
The infeasibility interface is exposed by the top-level `Cplex`
class as Cplex.solution.infeasibility. This constructor is not
meant to be used externally.
"""
super().__init__(cplex=parent._cplex, advanced=True)
[docs]
def bound_constraints(self, x, *args):
"""Returns the amount by which variable bounds are violated by x.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> c.solution.infeasibility.bound_constraints(c.solution.get_values(), 2)
0.0
>>> c.solution.infeasibility.bound_constraints(c.solution.get_values(), "x10")
0.0
>>> c.solution.infeasibility.bound_constraints(c.solution.get_values(), ["x10", 8])
[0.0, 0.0]
>>> bd = c.solution.infeasibility.bound_constraints(c.solution.get_values())
>>> bd[15]
0.0
"""
def getinfeas(a, b=self._cplex.variables.get_num() - 1):
return CPX_PROC.getcolinfeas(self._env._e, self._cplex._lp, x, a, b)
return apply_freeform_two_args(
getinfeas, self._cplex.variables._conv, args)
[docs]
def linear_constraints(self, x, *args):
"""Returns the amount by which a set of linear constraints are violated by x.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> sol_vals = c.solution.get_values()
>>> getrowinfeas = c.solution.infeasibility.linear_constraints
>>> abs(getrowinfeas(sol_vals, "c10"))
0.0
>>> abs(getrowinfeas(sol_vals, 7))
0.0
>>> [abs(x) for x in getrowinfeas(sol_vals, ["c13", 4])]
[0.0, 0.0]
>>> lconstraint = getrowinfeas(sol_vals)
>>> abs(lconstraint[5])
0.0
"""
def getinfeas(a, b=self._cplex.linear_constraints.get_num() - 1):
return CPX_PROC.getrowinfeas(self._env._e, self._cplex._lp, x, a, b)
return apply_freeform_two_args(
getinfeas, self._cplex.linear_constraints._conv, args)
[docs]
def quadratic_constraints(self, x, *args):
"""Returns the amount by which a set of quadratic constraints are violated by x.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("miqcp.lp")
>>> c.solve()
>>> getqconstrinfeas = c.solution.infeasibility.quadratic_constraints
>>> abs(getqconstrinfeas(c.solution.get_values(), 2)) < 1e-6
True
>>> abs(getqconstrinfeas(c.solution.get_values(), "QC3")) < 1e-6
True
>>> [abs(x) < 1e-6 for x in getqconstrinfeas(c.solution.get_values(), [1, "QC1"])]
[True, True]
>>> [abs(x) < 1e-6 for x in getqconstrinfeas(c.solution.get_values())]
[True, True, True, True]
"""
def getinfeas(a, b=self._cplex.quadratic_constraints.get_num() - 1):
return CPX_PROC.getqconstrinfeas(self._env._e, self._cplex._lp, x, a, b)
return apply_freeform_two_args(
getinfeas, self._cplex.quadratic_constraints._conv, args)
[docs]
def indicator_constraints(self, x, *args):
"""Returns the amount by which indicator constraints are violated by x.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> c.solution.infeasibility.indicator_constraints(c.solution.get_values(), 3)
0.0
>>> c.solution.infeasibility.indicator_constraints(c.solution.get_values(), "c21")
0.0
>>> c.solution.infeasibility.indicator_constraints(c.solution.get_values(), ["c21", 10])
[0.0, 0.0]
>>> iconstraint = c.solution.infeasibility.indicator_constraints(c.solution.get_values())
>>> iconstraint[5]
0.0
"""
def getinfeas(a, b=self._cplex.indicator_constraints.get_num() - 1):
return CPX_PROC.getindconstrinfeas(self._env._e, self._cplex._lp, x, a, b)
return apply_freeform_two_args(
getinfeas, self._cplex.indicator_constraints._conv, args)
[docs]
def SOS_constraints(self, x, *args):
"""Returns the amount by which SOS constraints are violated by x.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("miqcp.lp")
>>> c.solve()
>>> c.solution.infeasibility.SOS_constraints(c.solution.get_values(), 0)
0.0
>>> c.solution.infeasibility.SOS_constraints(c.solution.get_values(), "set1")
0.0
>>> c.solution.infeasibility.SOS_constraints(c.solution.get_values(), ["set1", 0])
[0.0, 0.0]
>>> c.solution.infeasibility.SOS_constraints(c.solution.get_values())
[0.0]
"""
def getinfeas(a, b=self._cplex.SOS.get_num() - 1):
return CPX_PROC.getsosinfeas(self._env._e, self._cplex._lp, x, a, b)
return apply_freeform_two_args(
getinfeas, self._cplex.SOS._conv, args)
[docs]
class CutType(ConstantClass):
"""Identifiers for types of cuts."""
# NB: If you edit these, look at MIPInfoCallback.cut_type too!
cover = _constants.CPX_CUT_COVER
GUB_cover = _constants.CPX_CUT_GUBCOVER
flow_cover = _constants.CPX_CUT_FLOWCOVER
clique = _constants.CPX_CUT_CLIQUE
fractional = _constants.CPX_CUT_FRAC
MIR = _constants.CPX_CUT_MIR
flow_path = _constants.CPX_CUT_FLOWPATH
disjunctive = _constants.CPX_CUT_DISJ
implied_bound = _constants.CPX_CUT_IMPLBD
zero_half = _constants.CPX_CUT_ZEROHALF
multi_commodity_flow = _constants.CPX_CUT_MCF
_local_cover = _constants.CPX_CUT_LOCALCOVER
_tighten = _constants.CPX_CUT_TIGHTEN
_objective_disjunctive = _constants.CPX_CUT_OBJDISJ
lift_and_project = _constants.CPX_CUT_LANDP
user = _constants.CPX_CUT_USER
table = _constants.CPX_CUT_TABLE
solution_pool = _constants.CPX_CUT_SOLNPOOL
local_implied_bound = _constants.CPX_CUT_LOCALIMPLBD
BQP = _constants.CPX_CUT_BQP
RLT = _constants.CPX_CUT_RLT
benders = _constants.CPX_CUT_BENDERS
__num_types = _constants.CPX_CUT_NUM_TYPES
[docs]
class MIPSolutionInterface(BaseInterface):
"""Methods for accessing solutions to a MIP."""
cut_type = CutType()
"""See `CutType()` """
[docs]
def __init__(self, parent):
"""Creates a new MIPSolutionInterface.
The MIP solution interface is exposed by the top-level `Cplex`
class as Cplex.solution.MIP. This constructor is not meant to
be used externally.
"""
super().__init__(cplex=parent._cplex, advanced=True)
[docs]
def get_best_objective(self):
"""Returns the currently best known bound of all the remaining
open nodes in a branch-and-cut tree.
It is computed for a minimization problem as the minimum
objective function value of all remaining unexplored nodes.
Similarly, it is computed for a maximization problem as the
maximum objective function value of all remaining unexplored
nodes.
For a regular MIP optimization, this value is also the best known
bound on the optimal solution value of the MIP problem. In fact,
when a problem has been solved to optimality, this value matches
the optimal solution value.
However, for the populate method, the value can also exceed the
optimal solution value if CPLEX has already solved the model to
optimality but continues to search for additional solutions.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> best_obj = c.solution.MIP.get_best_objective()
>>> abs(best_obj - 499.0) < 1e-6
True
"""
return CPX_PROC.getbestobjval(self._env._e, self._cplex._lp)
[docs]
def get_cutoff(self):
"""Returns the MIP cutoff value.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> cutoff = c.solution.MIP.get_cutoff()
>>> abs(cutoff - 499.0) < 1e-6
True
"""
return CPX_PROC.getcutoff(self._env._e, self._cplex._lp)
[docs]
def get_mip_relative_gap(self):
"""Returns the MIP relative gap.
See `CPXgetmiprelgap <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/mipapi/getmiprelgap.html>`_ in the Callable Library Reference
Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> c.solution.MIP.get_mip_relative_gap()
0.0
"""
return CPX_PROC.getmiprelgap(self._env._e, self._cplex._lp)
[docs]
def get_incumbent_node(self):
"""Returns the node number of the best solution found.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.parameters.randomseed.set(1)
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.parameters.threads.set(1)
>>> c.solve()
>>> c.solution.MIP.get_incumbent_node() >= 0
True
"""
return CPX_PROC.getnodeint(self._env._e, self._cplex._lp)
[docs]
def get_num_cuts(self, cut_type):
"""Returns the number of cuts of the specified type.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.parameters.randomseed.set(1)
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> ncuts = c.solution.MIP.get_num_cuts(
... c.solution.MIP.cut_type.zero_half)
"""
return CPX_PROC.getnumcuts(self._env._e, self._cplex._lp, cut_type)
[docs]
def get_subproblem_status(self):
"""Returns the solution status of the last subproblem optimization.
Returns an attribute of Cplex.solution.status if there was an
error termination where a subproblem could not be solved to
completion during mixed integer optimization. Otherwise 0
(zero) is returned if no error occurred.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> c.solution.MIP.get_subproblem_status()
0
"""
return CPX_PROC.getsubstat(self._env._e, self._cplex._lp)
[docs]
class BasisVarStatus(ConstantClass):
"""Status values returned by basis query methods."""
at_lower_bound = _constants.CPX_AT_LOWER
basic = _constants.CPX_BASIC
at_upper_bound = _constants.CPX_AT_UPPER
free_nonbasic = _constants.CPX_FREE_SUPER
[docs]
class BasisInterface(BaseInterface):
"""Methods for accessing the basis of a solution."""
status = BasisVarStatus()
"""See `BasisVarStatus()` """
[docs]
def __init__(self, parent):
"""Creates a new BasisInterface.
The basis interface is exposed by the top-level `Cplex` class as
Cplex.solution.basis. This constructor is not meant to be used
externally.
"""
super().__init__(cplex=parent._cplex, advanced=True)
[docs]
def get_basis(self):
"""Returns the status of structural and slack variables.
Returns a pair of lists of attributes of solution.basis.status.
The first lists the status of the structural variables (of length
equal to the number of variables), the second lists the status of
the slack variables (of length equal to the number of linear
constraints).
See `CPXgetbase <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/getbase.html>`_ in the Callable Library Reference Manual
for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> pair_of_lists = c.solution.basis.get_basis()
"""
return CPX_PROC.getbase(self._env._e, self._cplex._lp)
[docs]
def write(self, filename):
"""Writes the basis to a file.
See `CPXmbasewrite <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/mbasewrite.html>`_ in the Callable Library Reference
Manual and also `InitialInterface.read_basis()`.
Example:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> c.solution.basis.write("lpex.bas")
"""
CPX_PROC.mbasewrite(self._env._e, self._cplex._lp, filename)
[docs]
def get_basic_row_index(self, row):
"""Returns the position of a basic slack variable in the basis header.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> c.solution.basis.get_basic_row_index(2)
3
"""
return CPX_PROC.getijrow(self._env._e, self._cplex._lp, row, True)
[docs]
def get_basic_col_index(self, col):
"""Returns the position of a basic structural variable in the basis header.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> c.solution.basis.get_basic_col_index(2)
1
"""
return CPX_PROC.getijrow(self._env._e, self._cplex._lp, col, False)
[docs]
def get_primal_norms(self):
"""Returns norms from the primal steepest edge.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.parameters.preprocessing.presolve.set(c.parameters.preprocessing.presolve.values.off)
>>> c.parameters.simplex.pgradient.set(c.parameters.simplex.pgradient.values.steep)
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.primal)
>>> c.solve()
>>> pnorm = c.solution.basis.get_primal_norms()
>>> for i, j in zip(pnorm[1], [1.722656, 1.691406, 2.0, 1.062499]):
... abs(i - j) < 1e-6
...
True
True
True
True
"""
return CPX_PROC.getpnorms(self._env._e, self._cplex._lp)
[docs]
def get_dual_norms(self):
"""Returns norms from the dual steepest edge.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.dual)
>>> c.solve()
>>> c.solution.basis.get_dual_norms()
([1.0, 1.0, 1.0, 1.0], [1, 2, 3, -3])
"""
return CPX_PROC.getdnorms(self._env._e, self._cplex._lp)
[docs]
def get_basis_dual_norms(self):
"""Returns basis and dual norms.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.dual)
>>> c.solve()
>>> c.solution.basis.get_basis_dual_norms()
([2, 1, 1, 1], [0, 0, 1, 0], [1.0, 1.0, 1.0, 1.0])
"""
return CPX_PROC.getbasednorms(self._env._e, self._cplex._lp)
[docs]
def get_num_primal_superbasic(self):
"""Returns the number of primal superbasic variables.
"""
return CPX_PROC.getpsbcnt(self._env._e, self._cplex._lp)
[docs]
def get_num_dual_superbasic(self):
"""Returns the number of primal superbasic variables.
"""
return CPX_PROC.getdsbcnt(self._env._e, self._cplex._lp)
[docs]
class SensitivityInterface(BaseInterface):
"""Methods for sensitivity analysis."""
[docs]
def __init__(self, parent):
"""Creates a new SensitivityInterface.
The sensitivity interface is exposed by the top-level `Cplex`
class as Cplex.solution.sensitivity. This constructor is not
meant to be used externally.
"""
super().__init__(cplex=parent._cplex, advanced=True)
[docs]
def lower_bounds(self, *args):
"""Returns the sensitivity of a set of lower bounds.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> c.solution.sensitivity.lower_bounds(1)
(-1e+20, 17.5)
>>> c.solution.sensitivity.lower_bounds('x3')
(-1e+20, 42.5)
>>> c.solution.sensitivity.lower_bounds(["x3", 0])
[(-1e+20, 42.5), (-1e+20, 40.0)]
>>> c.solution.sensitivity.lower_bounds()
[(-1e+20, 40.0), (-1e+20, 17.5), (-1e+20, 42.5), (-1e+20, 0.625)]
"""
def sa(a, b=self._cplex.variables.get_num() - 1):
return unzip(CPX_PROC.boundsa_lower(self._env._e, self._cplex._lp, a, b))
return apply_freeform_two_args(
sa, self._cplex.variables._conv, args)
[docs]
def upper_bounds(self, *args):
"""Returns the sensitivity of a set of upper bounds.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> c.solution.sensitivity.upper_bounds(1)
(17.5, 1e+20)
>>> c.solution.sensitivity.upper_bounds("x3")
(42.5, 1e+20)
>>> bupper = c.solution.sensitivity.upper_bounds(["x3", 0])
>>> for i, j in zip(bupper, [(42.5, 1e+20), (36.428571, 155.0)]):
... abs(i[0] - j[0]) < 1e-6 and abs(i[1]- j[1]) < 1e-6
...
True
True
>>> bupper = c.solution.sensitivity.upper_bounds()
>>> for i, j in zip(bupper[3], (0.625, 1e+20)):
... abs(i - j) < 1e-6
...
True
True
"""
def sa(a, b=self._cplex.variables.get_num() - 1):
return unzip(CPX_PROC.boundsa_upper(self._env._e, self._cplex._lp, a, b))
return apply_freeform_two_args(
sa, self._cplex.variables._conv, args)
[docs]
def bounds(self, *args):
"""Returns the sensitivity of a set of both lower and upper bounds.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> c.solution.sensitivity.bounds(1)
(-1e+20, 17.5, 17.5, 1e+20)
>>> c.solution.sensitivity.bounds("x3")
(-1e+20, 42.5, 42.5, 1e+20)
>>> c.solution.sensitivity.bounds(["x3", 1])
[(-1e+20, 42.5, 42.5, 1e+20), (-1e+20, 17.5, 17.5, 1e+20)]
>>> bd = c.solution.sensitivity.bounds()
>>> bd[1]
(-1e+20, 17.5, 17.5, 1e+20)
"""
def sa(a, b=self._cplex.variables.get_num() - 1):
return unzip(CPX_PROC.boundsa(self._env._e, self._cplex._lp, a, b))
return apply_freeform_two_args(
sa, self._cplex.variables._conv, args)
[docs]
def objective(self, *args):
"""Returns the sensitivity of part of the objective function.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> c.solution.sensitivity.objective(1)
(-3.0, 5.0)
>>> c.solution.sensitivity.objective("x3")
(-1e+20, -2.0)
>>> c.solution.sensitivity.objective(["x3", 1])
[(-1e+20, -2.0), (-3.0, 5.0)]
>>> c.solution.sensitivity.objective()
[(-1e+20, 2.5), (-3.0, 5.0), (-1e+20, -2.0), (0.0, 4.0)]
"""
def sa(a, b=self._cplex.variables.get_num() - 1):
return unzip(CPX_PROC.objsa(self._env._e, self._cplex._lp, a, b))
return apply_freeform_two_args(
sa, self._cplex.variables._conv, args)
[docs]
def rhs(self, *args):
"""Returns the sensitivity of the righthand side of a set of linear constraints.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> rhssa = c.solution.sensitivity.rhs(1)
>>> for i, j in zip(rhssa, (20.0, 46.666666)):
... abs(i - j) < 1e-6
...
True
True
>>> c.solution.sensitivity.rhs("c3")
(-1e+20, 112.5)
>>> rhssa = c.solution.sensitivity.rhs(["c3", 1])
>>> for i, j in zip(rhssa, [(-1e+20, 112.5), (20.0, 46.666666)]):
... abs(i[0] - j[0]) < 1e-6 and abs(i[1]- j[1]) < 1e-6
...
True
True
>>> rhssa = c.solution.sensitivity.rhs()
>>> for i, j in zip(rhssa[3], (-1e+20, 42.5)):
... abs(i - j) < 1e-6
...
True
True
"""
def sa(a, b=self._cplex.linear_constraints.get_num() - 1):
return unzip(CPX_PROC.rhssa(self._env._e, self._cplex._lp, a, b))
return apply_freeform_two_args(
sa, self._cplex.linear_constraints._conv, args)
[docs]
class FilterType(ConstantClass):
"""Attributes define the filter types."""
diversity = _constants.CPX_SOLNPOOL_FILTER_DIVERSITY
range = _constants.CPX_SOLNPOOL_FILTER_RANGE
[docs]
class SolnPoolFilterInterface(BaseInterface):
"""Methods for solution pool filters."""
type = FilterType()
"""See `FilterType()` """
[docs]
def __init__(self, parent):
"""Creates a new SolnPoolFilterInterface.
The solution pool filter interface is exposed by the top-level
`Cplex` class as Cplex.solution.pool.filter. This constructor
is not meant to be used externally.
"""
super().__init__(cplex=parent._cplex, advanced=True,
getindexfunc=CPX_PROC.getsolnpoolfilterindex)
def _add_diversity_filter(self, lb, ub, expression, weights, name):
"""non-public"""
ind, val = unpack_pair(expression)
validate_arg_lengths(
[ind, val, weights],
extra_msg=": expression, weights"
)
CPX_PROC.addsolnpooldivfilter(
self._env._e, self._cplex._lp, lb, ub,
self._cplex.variables._conv(ind),
weights, val, name)
[docs]
def add_diversity_filter(self, lb, ub, expression, weights=None,
name=''):
"""Adds a diversity filter to the solution pool.
The arguments determine, in order,
the lower bound (float)
the upper bound (float)
the variables and values it takes as either a SparsePair or a
list of two lists.
a set of weights (a list of floats with the same length as
expression). If an empty list is given, then weights of 1.0
(one) will be used.
name (string)
Returns the index of the added diversity filter.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> c.solution.pool.filter.add_diversity_filter(
... 300, 600, [['x1','x2'], [1,1]], [2,1], "")
0
"""
(weights,) = init_list_args(weights)
return self._add_single(self.get_num, self._add_diversity_filter,
lb, ub, expression, weights, name)
def _add_range_filter(self, lb, ub, expression, name):
"""non-public"""
ind, val = unpack_pair(expression)
CPX_PROC.addsolnpoolrngfilter(
self._env._e, self._cplex._lp, lb, ub,
self._cplex.variables._conv(ind),
val, name)
[docs]
def add_range_filter(self, lb, ub, expression, name=''):
"""Adds a range filter to the solution pool.
The arguments determine, in order,
the lower bound (float)
the upper bound (float)
the variables and values it takes as either a SparsePair or a
list of two lists.
name (string)
Returns the index of the added range filter.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> c.solution.pool.filter.add_range_filter(
... 300, 600, [['x1','x2'], [1,1]], "")
0
"""
return self._add_single(self.get_num, self._add_range_filter,
lb, ub, expression, name)
[docs]
def get_diversity_filters(self, *args):
"""Returns a set of diversity filters.
Returns filters as pairs of (SparsePair, weights), where
weights is a list of floats.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ['x','y'], types = ["BB"])
>>> f = cplex.SparsePair(ind = ['x'],val = [1.0])
>>> [c.solution.pool.filter.add_diversity_filter(
... 0, 1, f, [1], str(i))
... for i in range(2)]
[0, 1]
>>> c.solution.pool.filter.get_diversity_filters(0)
(SparsePair(ind = [0], val = [1.0]), [1.0])
>>> c.solution.pool.filter.get_diversity_filters("1")
(SparsePair(ind = [0], val = [1.0]), [1.0])
>>> c.solution.pool.filter.get_diversity_filters([0, "1"])
[(SparsePair(ind = [0], val = [1.0]), [1.0]), (SparsePair(ind = [0], val = [1.0]), [1.0])]
>>> c.solution.pool.filter.get_diversity_filters()
[(SparsePair(ind = [0], val = [1.0]), [1.0]), (SparsePair(ind = [0], val = [1.0]), [1.0])]
"""
def getflt(a):
ret = CPX_PROC.getsolnpooldivfilter(
self._env._e, self._cplex._lp, a)
return (SparsePair(ret[2], ret[4]), ret[3])
return apply_freeform_one_arg(
getflt, self._conv, self.get_num(), args)
[docs]
def get_range_filters(self, *args):
"""Returns a set of range filters.
Returns filters as SparsePair instances.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ['x','y'], types = ["II"])
>>> f = cplex.SparsePair(ind = ['x'],val = [1.0])
>>> [c.solution.pool.filter.add_range_filter(
... 0.0, 1.0, f, str(i)) for i in range(2)]
[0, 1]
>>> c.solution.pool.filter.get_range_filters(0)
SparsePair(ind = [0], val = [1.0])
>>> c.solution.pool.filter.get_range_filters("1")
SparsePair(ind = [0], val = [1.0])
>>> c.solution.pool.filter.get_range_filters([0, "1"])
[SparsePair(ind = [0], val = [1.0]), SparsePair(ind = [0], val = [1.0])]
>>> c.solution.pool.filter.get_range_filters()
[SparsePair(ind = [0], val = [1.0]), SparsePair(ind = [0], val = [1.0])]
"""
def getflt(a):
ret = CPX_PROC.getsolnpoolrngfilter(
self._env._e, self._cplex._lp, a)
return SparsePair(ret[2], ret[3])
return apply_freeform_one_arg(
getflt, self._conv, self.get_num(), args)
def _getfilter_constant(self, which):
filter_type = self.get_types(which)
if filter_type == self.type.diversity:
func = CPX_PROC.getsolnpooldivfilter_constant
else:
assert filter_type == self.type.range
func = CPX_PROC.getsolnpoolrngfilter_constant
return func(self._env._e, self._cplex._lp, which)
[docs]
def get_bounds(self, *args):
"""Returns (lb, ub) pairs for a set of filters.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=['x', 'y'], types=["BB"])
>>> f = cplex.SparsePair(ind=['x'], val=[1.0])
>>> [c.solution.pool.filter.add_diversity_filter(
... 0, 1, f, [1], "div{0}".format(i)) for i in range(2)]
[0, 1]
>>> [c.solution.pool.filter.add_range_filter(
... 0, 1, f, "rng{0}".format(i)) for i in range(2)]
[2, 3]
>>> c.solution.pool.filter.get_bounds(0)
(0.0, 1.0)
>>> c.solution.pool.filter.get_bounds("rng0")
(0.0, 1.0)
>>> c.solution.pool.filter.get_bounds(["div0", 2])
[(0.0, 1.0), (0.0, 1.0)]
>>> c.solution.pool.filter.get_bounds()
[(0.0, 1.0), (0.0, 1.0), (0.0, 1.0), (0.0, 1.0)]
"""
def getbds(which):
lb, ub, _ = self._getfilter_constant(which)
return (lb, ub)
return apply_freeform_one_arg(
getbds, self._conv, self.get_num(), args)
[docs]
def get_num_nonzeros(self, *args):
"""Returns the number of variables specified by a set of filters.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=['x', 'y'], types=["BB"])
>>> f = cplex.SparsePair(ind=['x'], val=[1.0])
>>> [c.solution.pool.filter.add_diversity_filter(
... 0, 1, f, [1], "div{0}".format(i)) for i in range(2)]
[0, 1]
>>> [c.solution.pool.filter.add_range_filter(
... 0, 1, f, "rng{0}".format(i)) for i in range(2)]
[2, 3]
>>> c.solution.pool.filter.get_num_nonzeros(0)
1
>>> c.solution.pool.filter.get_num_nonzeros("rng0")
1
>>> c.solution.pool.filter.get_num_nonzeros(["div0", 2])
[1, 1]
>>> c.solution.pool.filter.get_num_nonzeros()
[1, 1, 1, 1]
"""
def getnnz(which):
_, _, nnz = self._getfilter_constant(which)
return nnz
return apply_freeform_one_arg(
getnnz, self._conv, self.get_num(), args)
[docs]
def delete(self, *args):
"""Deletes filters from the problem.
There are four forms by which filters.delete may be called.
filters.delete()
deletes all filters from the problem.
filters.delete(i)
i must be a filter name or index. Deletes the filter whose
index or name is i.
filters.delete(s)
s must be a sequence of filter names or indices. Deletes
the filters with names or indices contained within s.
Equivalent to [filters.delete(i) for i in s].
filters.delete(begin, end)
begin and end must be filter indices or filter names. Deletes
the filters with indices between begin and end, inclusive of
end. Equivalent to filters.delete(range(begin, end + 1)). This
will give the best performance when deleting batches of
filters.
See `CPXdelsolnpoolfilters <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/mipapi/delsolnpoolfilters.html>`_ in the Callable Library
Reference Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names=['x', 'y'], types=['II'])
>>> f = cplex.SparsePair(ind=['x'], val=[1.0])
>>> [c.solution.pool.filter.add_range_filter(
... 0.0, 1.0, f, str(i)) for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.solution.pool.filter.get_num()
10
>>> c.solution.pool.filter.delete(8)
>>> c.solution.pool.filter.get_names()
['0', '1', '2', '3', '4', '5', '6', '7', '9']
>>> c.solution.pool.filter.delete('1', 3)
>>> c.solution.pool.filter.get_names()
['0', '4', '5', '6', '7', '9']
>>> c.solution.pool.filter.delete([2, '0', 5])
>>> c.solution.pool.filter.get_names()
['4', '6', '7']
>>> c.solution.pool.filter.delete()
>>> c.solution.pool.filter.get_names()
[]
"""
def _delete(begin, end=None):
CPX_PROC.delsolnpoolfilters(self._env._e, self._cplex._lp,
begin, end)
delete_set_by_range(_delete, self._conv, self.get_num(), *args)
[docs]
def get_types(self, *args):
"""Returns the types of a set of filters.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ['x','y'], types = ["II"])
>>> f = cplex.SparsePair(ind = ['x'],val = [1.0])
>>> [c.solution.pool.filter.add_range_filter(
... 0.0, 1.0, f, str(i)) for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.solution.pool.filter.get_types(3)
2
>>> c.solution.pool.filter.get_types("5")
2
>>> c.solution.pool.filter.get_types([2, "8"])
[2, 2]
>>> c.solution.pool.filter.get_types()
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
"""
def gettype(a):
return CPX_PROC.getsolnpoolfiltertype(self._env._e, self._cplex._lp, a)
return apply_freeform_one_arg(
gettype, self._conv, self.get_num(), args)
[docs]
def get_names(self, *args):
"""Returns the names of filters, given their indices.
There are four forms by which solution.pool.filter.get_names may be called.
solution.pool.filter.get_names()
return the names of all solution pool filters from the problem.
solution.pool.filter.get_names(i)
i must be a solution filter index. Returns the name of row i.
solution.pool.filter.get_names(s)
s must be a sequence of row indices. Returns the names of
the solution pool filters with indices the members of s.
Equivalent to [solution.pool.filter.get_names(i) for i in s]
solution.pool.filter.get_names(begin, end)
begin and end must be solution filter indices. Returns the
names of the solution pool filter with indices between begin
and end, inclusive of end. Equivalent to
solution.pool.filter.get_names(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = ['x','y'], types = ["II"])
>>> f = cplex.SparsePair(ind = ['x'],val = [1.0])
>>> [c.solution.pool.filter.add_range_filter(
... 0.0, 1.0, f, str(i)) for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.solution.pool.filter.get_names()
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
>>> c.solution.pool.filter.get_names(6)
'6'
>>> c.solution.pool.filter.get_names([5, 3])
['5', '3']
>>> c.solution.pool.filter.get_names(3, 5)
['3', '4', '5']
"""
def getname(a):
return CPX_PROC.getsolnpoolfiltername(
self._env._e, self._cplex._lp, a)
return apply_freeform_one_arg(
getname, self._conv, self.get_num(), args)
[docs]
def write(self, filename):
"""Writes the filters to a file.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> c.solution.pool.filter.add_range_filter(
... 300, 600, [['x1','x2'], [1,1]], "")
0
>>> c.solution.pool.filter.write("ind.flt")
"""
CPX_PROC.fltwrite(self._env._e, self._cplex._lp, filename)
[docs]
def read(self, filename):
"""Reads filters from a file.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> c.solution.pool.filter.add_range_filter(
... 300, 600, [['x1','x2'], [1,1]], "")
0
>>> c.solution.pool.filter.write("ind.flt")
>>> c.solution.pool.filter.read("ind.flt")
"""
CPX_PROC.readcopysolnpoolfilters(self._env._e, self._cplex._lp,
filename)
[docs]
def get_num(self):
"""Returns the number of filters in the problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> c.solution.pool.filter.add_range_filter(
... 300, 600, [['x1','x2'], [1,1]], "")
0
>>> c.solution.pool.filter.get_num()
1
"""
return CPX_PROC.getsolnpoolnumfilters(self._env._e, self._cplex._lp)
[docs]
class QualityMetric(ConstantClass):
"""Measures of solution quality."""
max_primal_infeasibility = _constants.CPX_MAX_PRIMAL_INFEAS
max_scaled_primal_infeasibility = _constants.CPX_MAX_SCALED_PRIMAL_INFEAS
sum_primal_infeasibilities = _constants.CPX_SUM_PRIMAL_INFEAS
sum_scaled_primal_infeasibilities = _constants.CPX_SUM_SCALED_PRIMAL_INFEAS
max_dual_infeasibility = _constants.CPX_MAX_DUAL_INFEAS
max_scaled_dual_infeasibility = _constants.CPX_MAX_SCALED_DUAL_INFEAS
sum_dual_infeasibilities = _constants.CPX_SUM_DUAL_INFEAS
sum_scaled_dual_infeasibilities = _constants.CPX_SUM_SCALED_DUAL_INFEAS
max_int_infeasibility = _constants.CPX_MAX_INT_INFEAS
sum_integer_infeasibilities = _constants.CPX_SUM_INT_INFEAS
max_primal_residual = _constants.CPX_MAX_PRIMAL_RESIDUAL
max_scaled_primal_residual = _constants.CPX_MAX_SCALED_PRIMAL_RESIDUAL
sum_primal_residual = _constants.CPX_SUM_PRIMAL_RESIDUAL
sum_scaled_primal_residual = _constants.CPX_SUM_SCALED_PRIMAL_RESIDUAL
max_dual_residual = _constants.CPX_MAX_DUAL_RESIDUAL
max_scaled_dual_residual = _constants.CPX_MAX_SCALED_DUAL_RESIDUAL
sum_dual_residual = _constants.CPX_SUM_DUAL_RESIDUAL
sum_scaled_dual_residual = _constants.CPX_SUM_SCALED_DUAL_RESIDUAL
max_comp_slack = _constants.CPX_MAX_COMP_SLACK
sum_comp_slack = _constants.CPX_SUM_COMP_SLACK
max_x = _constants.CPX_MAX_X
max_scaled_x = _constants.CPX_MAX_SCALED_X
max_pi = _constants.CPX_MAX_PI
max_scaled_pi = _constants.CPX_MAX_SCALED_PI
max_slack = _constants.CPX_MAX_SLACK
max_scaled_slack = _constants.CPX_MAX_SCALED_SLACK
max_reduced_cost = _constants.CPX_MAX_RED_COST
max_scaled_reduced_cost = _constants.CPX_MAX_SCALED_RED_COST
sum_x = _constants.CPX_SUM_X
sum_scaled_x = _constants.CPX_SUM_SCALED_X
sum_pi = _constants.CPX_SUM_PI
sum_scaled_pi = _constants.CPX_SUM_SCALED_PI
sum_slack = _constants.CPX_SUM_SLACK
sum_scaled_slack = _constants.CPX_SUM_SCALED_SLACK
sum_reduced_cost = _constants.CPX_SUM_RED_COST
sum_scaled_reduced_cost = _constants.CPX_SUM_SCALED_RED_COST
kappa = _constants.CPX_KAPPA
objective_gap = _constants.CPX_OBJ_GAP
dual_objective = _constants.CPX_DUAL_OBJ
primal_objective = _constants.CPX_PRIMAL_OBJ
max_quadratic_primal_residual = _constants.CPX_MAX_QCPRIMAL_RESIDUAL
sum_quadratic_primal_residual = _constants.CPX_SUM_QCPRIMAL_RESIDUAL
max_quadratic_slack_infeasibility = _constants.CPX_MAX_QCSLACK_INFEAS
sum_quadratic_slack_infeasibility = _constants.CPX_SUM_QCSLACK_INFEAS
max_quadratic_slack = _constants.CPX_MAX_QCSLACK
sum_quadratic_slack = _constants.CPX_SUM_QCSLACK
max_indicator_slack_infeasibility = _constants.CPX_MAX_INDSLACK_INFEAS
sum_indicator_slack_infeasibility = _constants.CPX_SUM_INDSLACK_INFEAS
max_pwl_slack_infeasibility = _constants.CPX_MAX_PWLSLACK_INFEAS
sum_pwl_slack_infeasibility = _constants.CPX_SUM_PWLSLACK_INFEAS
exact_kappa = _constants.CPX_EXACT_KAPPA
kappa_stable = _constants.CPX_KAPPA_STABLE
kappa_suspicious = _constants.CPX_KAPPA_SUSPICIOUS
kappa_unstable = _constants.CPX_KAPPA_UNSTABLE
kappa_illposed = _constants.CPX_KAPPA_ILLPOSED
kappa_max = _constants.CPX_KAPPA_MAX
kappa_attention = _constants.CPX_KAPPA_ATTENTION
@contextmanager
def _temp_results_stream(cpx, temp_stream):
old_results_stream = cpx._env._get_results_stream()
try:
yield cpx._env.set_results_stream(temp_stream)
finally:
cpx._env.set_results_stream(old_results_stream._file)
[docs]
class QualityMetrics():
"""A class containing measures of the quality of a solution.
The __str__ method of this class prints all available measures of
the quality of the solution in human readable form.
This class may have a different set of data members depending on
the optimization algorithm used and the quality metrics that are
available.
An instance of this class always has the member quality_type,
which is one of the following strings:
* "feasopt"
* "simplex"
* "quadratically_constrained"
* "barrier"
* "MIP"
If self.quality_type is "feasopt" this instance has the following
members:
* scaled
* max_x
* max_bound_infeas
* max_Ax_minus_b
* max_slack
If self.scaled is 1, this instance also has the members:
* max_scaled_x
* max_scaled_bound_infeas
* max_scaled_Ax_minus_b
* max_scaled_slack
If self.quality_type is "simplex" this instance has the following
members:
* scaled
* max_x
* max_pi
* max_reduced_cost
* max_bound_infeas
* max_reduced_cost_infeas
* max_Ax_minus_b
* max_c_minus_Bpi
* max_slack
If self.scaled is 1, this instance also has the members:
* max_scaled_x
* max_scaled_pi
* max_scaled_reduced_cost
* max_scaled_bound_infeas
* max_scaled_reduced_cost_infeas
* max_scaled_Ax_minus_b
* max_scaled_c_minus_Bpi
* max_scaled_slack
If the condition number of the final basis is available, this
instance has the member:
* kappa
If self.quality_type is "quadratically_constrained" this instance
has the following members:
* objective
* norm_total
* norm_max
* error_Ax_b_total
* error_Ax_b_max
* error_xQx_dx_f_total
* error_xQx_dx_f_max
* x_bound_error_total
* x_bound_error_max
* slack_bound_error_total
* slack_bound_error_max
* quadratic_slack_bound_error_total
* quadratic_slack_bound_error_max
* normalized_error_max
If self.quality_type is "barrier" this instance has the following
members:
* primal_objective
* dual_objective
* duality_gap
* complementarity_total
* column_complementarity_total
* column_complementarity_max
* row_complementarity_total
* row_complementarity_max
* primal_norm_total
* primal_norm_max
* dual_norm_total
* dual_norm_max
* primal_error_total
* primal_error_max
* dual_error_total
* dual_error_max
* primal_x_bound_error_total
* primal_x_bound_error_max
* primal_slack_bound_error_total
* primal_slack_bound_error_max
* dual_pi_bound_error_total
* dual_pi_bound_error_max
* dual_reduced_cost_bound_error_total
* dual_reduced_cost_bound_error_max
* primal_normalized_error
* dual_normalized_error
If self.quality_type is "MIP" and this instance was generated for
a specific member of the solution pool, it has the members:
* solution_name
* num_solutions
If self.quality_type is "MIP", this instance was not generated for
a specific member of the solution pool, and kappa statistics are
available, it has the members:
* max_kappa
* pct_kappa_stable
* pct_kappa_suspicious
* pct_kappa_unstable
* pct_kappa_illposed
* kappa_attention
If self.quality_type is "MIP" and this instance was generated for
the incumbent solution, it has the members:
* solver
* objective
* x_norm_total
* x_norm_max
* error_Ax_b_total
* error_Ax_b_max
* x_bound_error_total
* x_bound_error_max
* integrality_error_total
* integrality_error_max
* slack_bound_error_total
* slack_bound_error_max
If in addition the problem this instance was generated for has
indicator constraints, it has the members:
* indicator_slack_bound_error_total
* indicator_slack_bound_error_max
if in addition the problem this instance was generated for has
piecewise linear constraints, it has the members:
* piecewise_linear_error_total
* piecewise_linear_error_max
If solver is "MIQCP" this instance also has the members:
* error_xQx_dx_f_total
* error_xQx_dx_f_max
* quadratic_slack_bound_error_total
* quadratic_slack_bound_error_max
See also `SolutionInterface.get_quality_metrics` and
`SolnPoolInterface.get_quality_metrics`.
"""
[docs]
def __init__(self, c, soln=-1):
idata, data = CPX_PROC.getqualitymetrics(c._env._e, c._lp, soln)
# We get the "to string" from showquality by temporarily
# hijacking the results stream.
with closing(StringIO()) as output, \
_temp_results_stream(c, output): # noqa: E127
CPX_PROC.showquality(c._env._e, c._lp, soln)
self._tostring = output.getvalue()
if idata[0] == 0:
self.quality_type = "feasopt"
self.scaled = idata[1] == 1
if idata[2] == 1:
self.max_bound_infeas = data[0]
if self.scaled:
self.max_scaled_bound_infeas = data[1]
else:
self.max_bound_infeas = 0.0
if self.scaled:
self.max_scaled_bound_infeas = 0.0
if idata[3] == 1:
self.max_Ax_minus_b = data[2]
if self.scaled:
self.max_scaled_Ax_minus_b = data[3]
else:
self.max_Ax_minus_b = 0.0
if self.scaled:
self.max_scaled_Ax_minus_b = 0.0
self.max_x = data[4]
if self.scaled:
self.max_scaled_x = data[5]
if idata[4] == 1:
self.max_slack = data[6]
if self.scaled:
self.max_scaled_slack = data[7]
else:
self.max_slack = 0.0
if self.scaled:
self.max_scaled_slack = 0.0
elif idata[0] == 1:
self.quality_type = "simplex"
self.scaled = idata[1] == 1
if idata[2] == 1:
self.max_bound_infeas = data[0]
if self.scaled:
self.max_scaled_bound_infeas = data[1]
else:
self.max_bound_infeas = 0.0
if self.scaled:
self.max_scaled_bound_infeas = 0.0
if idata[3] == 1:
self.max_reduced_cost_infeas = data[2]
if self.scaled:
self.max_scaled_reduced_cost_infeas = data[3]
else:
self.max_reduced_cost_infeas = 0.0
if self.scaled:
self.max_scaled_reduced_cost_infeas = 0.0
if idata[6] == 1:
self.max_Ax_minus_b = data[4]
if self.scaled:
self.max_scaled_Ax_minus_b = data[5]
else:
self.max_Ax_minus_b = 0.0
if self.scaled:
self.max_scaled_Ax_minus_b = 0.0
if idata[7] == 1:
self.max_c_minus_Bpi = data[6]
if self.scaled:
self.max_scaled_c_minus_Bpi = data[7]
else:
self.max_c_minus_Bpi = 0.0
if self.scaled:
self.max_scaled_c_minus_Bpi = 0.0
self.max_x = data[8]
if self.scaled:
self.max_scaled_x = data[9]
if idata[8] == 1:
self.max_slack = data[10]
if self.scaled:
self.max_scaled_slack = data[11]
else:
self.max_slack = 0.0
if self.scaled:
self.max_scaled_slack = 0.0
self.max_pi = data[12]
if self.scaled:
self.max_scaled_pi = data[13]
self.max_reduced_cost = data[14]
if self.scaled:
self.max_scaled_reduced_cost = data[15]
if idata[9] == 1:
self.kappa = data[16]
elif idata[0] == 2:
self.quality_type = "quadratically_constrained"
self.objective = data[0]
self.norm_total = data[1]
self.norm_max = data[2]
self.error_Ax_b_total = data[3]
self.error_Ax_b_max = data[4]
self.error_xQx_dx_f_total = data[5]
self.error_xQx_dx_f_max = data[6]
self.x_bound_error_total = data[7]
self.x_bound_error_max = data[8]
self.slack_bound_error_total = data[9]
self.slack_bound_error_max = data[10]
self.quadratic_slack_bound_error_total = data[11]
self.quadratic_slack_bound_error_max = data[12]
self.normalized_error_max = data[13]
elif idata[0] == 3:
self.quality_type = "barrier"
self.primal_objective = data[0]
self.dual_objective = data[1]
self.duality_gap = data[2]
self.complementarity_total = data[3]
self.column_complementarity_total = data[4]
self.column_complementarity_max = data[5]
self.row_complementarity_total = data[6]
self.row_complementarity_max = data[7]
self.primal_norm_total = data[8]
self.primal_norm_max = data[9]
self.dual_norm_total = data[10]
self.dual_norm_max = data[11]
self.primal_error_total = data[12]
self.primal_error_max = data[13]
self.dual_error_total = data[14]
self.dual_error_max = data[15]
self.primal_x_bound_error_total = data[16]
self.primal_x_bound_error_max = data[17]
self.primal_slack_bound_error_total = data[18]
self.primal_slack_bound_error_max = data[19]
self.dual_pi_bound_error_total = data[20]
self.dual_pi_bound_error_max = data[21]
self.dual_reduced_cost_bound_error_total = data[22]
self.dual_reduced_cost_bound_error_max = data[23]
self.primal_normalized_error = data[24]
self.dual_normalized_error = data[25]
elif idata[0] == 4:
self.quality_type = "MIP"
if soln >= 0:
self.solution_name = c.solution.pool.get_names(soln)
self.num_solutions = c.solution.pool.get_num()
if idata[4] == 1:
if idata[2] == 0:
self.solver = "MILP"
elif idata[2] == 1:
self.solver = "MIQP"
elif idata[2] == 2:
self.solver = "MIQCP"
self.error_xQx_dx_f_total = data[6]
self.error_xQx_dx_f_max = data[7]
self.quadratic_slack_bound_error_total = data[14]
self.quadratic_slack_bound_error_max = data[15]
self.objective = data[1]
self.x_norm_total = data[2]
self.x_norm_max = data[3]
self.error_Ax_b_total = data[4]
self.error_Ax_b_max = data[5]
self.x_bound_error_total = data[8]
self.x_bound_error_max = data[9]
self.integrality_error_total = data[10]
self.integrality_error_max = data[11]
self.slack_bound_error_total = data[12]
self.slack_bound_error_max = data[13]
if idata[3] == 1:
self.indicator_slack_bound_error_total = data[16]
self.indicator_slack_bound_error_max = data[17]
if idata[5] == 1:
self.piecewise_linear_error_total = data[24]
self.piecewise_linear_error_max = data[25]
if idata[1] == 1:
self.max_kappa = data[18]
self.pct_kappa_stable = data[19]
self.pct_kappa_suspicious = data[20]
self.pct_kappa_unstable = data[21]
self.pct_kappa_illposed = data[22]
self.kappa_attention = data[23]
[docs]
def __str__(self):
"""Returns a string containing quality metrics in human readable form."""
# See __init__ (above) to see how this is constructed.
return self._tostring
[docs]
class SolnPoolInterface(BaseInterface):
"""Methods for accessing the solution pool."""
incumbent = _constants.CPX_INCUMBENT_ID
"""See `_constants.CPX_INCUMBENT_ID` """
quality_metric = QualityMetric()
"""See `QualityMetric()` """
[docs]
def __init__(self, parent):
"""Creates a new SolnPoolInterface.
The solution pool interface is exposed by the top-level `Cplex`
class as Cplex.solution.pool. This constructor is not meant to
be used externally.
"""
super().__init__(cplex=parent._cplex, advanced=True,
getindexfunc=CPX_PROC.getsolnpoolsolnindex)
self.filter = SolnPoolFilterInterface(self)
"""See `SolnPoolFilterInterface()` """
[docs]
def get_objective_value(self, soln):
"""Returns the objective value for a member of the solution pool.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> obj_val = c.solution.pool.get_objective_value(0)
>>> abs(obj_val - 499.0) < 1e-6
True
"""
if not isinstance(soln, int):
soln = self.get_indices(soln)
return CPX_PROC.getsolnpoolobjval(self._env._e, self._cplex._lp, soln)
[docs]
def get_values(self, soln, *args):
"""Returns the values of a set of variables for a given solution.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> c.parameters.randomseed.set(1)
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> v1 = c.solution.pool.get_values(1, 2)
>>> v2 = c.solution.pool.get_values(1, "x2")
>>> somevals = c.solution.pool.get_values(1, [2, "x2"])
>>> v1 == somevals[0], v2 == somevals[1]
(True, True)
>>> allvals = c.solution.pool.get_values(1)
>>> v1 == allvals[2]
True
"""
if not isinstance(soln, int):
soln = self.get_indices(soln)
def getx(a, b=self._cplex.variables.get_num() - 1):
return CPX_PROC.getsolnpoolx(self._env._e, self._cplex._lp, soln, a, b)
return apply_freeform_two_args(
getx, self._cplex.variables._conv, args)
[docs]
def get_linear_slacks(self, soln, *args):
"""Returns a set of linear slacks for a given solution.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> s1 = c.solution.pool.get_linear_slacks(1, 1)
>>> s2 = c.solution.pool.get_linear_slacks(1, "c2")
>>> someslacks = c.solution.pool.get_linear_slacks(1, [1, "c2"])
>>> s1 == someslacks[0], s2 == someslacks[1]
(True, True)
>>> allslacks = c.solution.pool.get_linear_slacks(1)
>>> s1 == allslacks[1]
True
"""
if not isinstance(soln, int):
soln = self.get_indices(soln)
def getslacks(a, b=self._cplex.linear_constraints.get_num() - 1):
return CPX_PROC.getsolnpoolslack(self._env._e, self._cplex._lp, soln, a, b)
return apply_freeform_two_args(
getslacks, self._cplex.linear_constraints._conv, args)
[docs]
def get_quadratic_slacks(self, soln, *args):
"""Returns a set of quadratic slacks for a given solution.
Can be called by four forms.
>>> import cplex
>>> c = cplex.Cplex()
>>> c.parameters.randomseed.set(1)
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("miqcp.lp")
>>> c.solve()
>>> var = c.solution.pool.get_quadratic_slacks(1, 1)
>>> var = c.solution.pool.get_quadratic_slacks(1, "QC3")
>>> vars = c.solution.pool.get_quadratic_slacks(1, ["QC3", 1])
>>> vars = c.solution.pool.get_quadratic_slacks(1)
"""
if not isinstance(soln, int):
soln = self.get_indices(soln)
def getqslacks(a, b=self._cplex.quadratic_constraints.get_num() - 1):
return CPX_PROC.getsolnpoolqconstrslack(self._env._e, self._cplex._lp, soln, a, b)
return apply_freeform_two_args(
getqslacks, self._cplex.quadratic_constraints._conv, args)
[docs]
def get_integer_quality(self, soln, which):
"""Returns the integer quality of a given solution.
The integer quality of a solution can either be a single attribute of
solution.pool.quality_metrics or a sequence of such
attributes.
Note
This corresponds to the CPLEX callable library function
CPXgetsolnpoolintquality.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> quality_metric = c.solution.pool.quality_metric
>>> misi = quality_metric.max_indicator_slack_infeasibility
>>> c.solution.pool.get_integer_quality(1, misi)
0
"""
if not isinstance(soln, int):
soln = self.get_indices(soln)
if isinstance(which, int):
return CPX_PROC.getsolnpoolintquality(
self._env._e, self._cplex._lp, soln, which)
return [CPX_PROC.getsolnpoolintquality(self._env._e,
self._cplex._lp, soln, a)
for a in which]
[docs]
def get_float_quality(self, soln, which):
"""Returns the float quality of a given solution.
The float quality of a solution can either be a single attribute of
solution.pool.quality_metrics or a sequence of such
attributes.
Note
This corresponds to the CPLEX callable library function
CPXgetsolnpooldblquality.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> qual = c.solution.pool.get_float_quality(1,
c.solution.pool.quality_metric.max_indicator_slack_infeasibility)
>>> abs(qual) < 1.e-6
True
"""
if not isinstance(soln, int):
soln = self.get_indices(soln)
if isinstance(which, int):
return CPX_PROC.getsolnpooldblquality(
self._env._e, self._cplex._lp, soln, which)
return [CPX_PROC.getsolnpooldblquality(self._env._e,
self._cplex._lp, soln, a)
for a in which]
[docs]
def get_mean_objective_value(self):
"""Returns the average among the objective values in the solution pool.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.parameters.randomseed.set(1)
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> mov = c.solution.pool.get_mean_objective_value()
"""
return CPX_PROC.getsolnpoolmeanobjval(self._env._e, self._cplex._lp)
[docs]
def delete(self, *args):
"""Deletes solutions from the solution pool.
There are four forms by which pool.delete may be called.
pool.delete()
deletes all solutions from the problem.
pool.delete(i)
i must be a solution name or index. Deletes the solution
whose index or name is i.
pool.delete(s)
s must be a sequence of solution names or indices. Deletes
the solutions with names or indices contained within s.
Equivalent to [pool.delete(i) for i in s].
pool.delete(begin, end)
begin and end must be solution indices or solution names.
Deletes the solutions with indices between begin and end,
inclusive of end. Equivalent to
pool.delete(range(begin, end + 1)). This will give the best
performance when deleting batches of solutions from the
solution pool.
See `CPXdelsolnpoolsolns <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/mipapi/delsolnpoolsolns.html>`_ in the Callable Library
Reference Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.parameters.randomseed.set(1)
>>> c.parameters.mip.limits.populate.set(5)
>>> c.populate_solution_pool()
>>> names = c.solution.pool.get_names()
>>> c.solution.pool.delete(1)
>>> n = c.solution.pool.get_names()
>>> del names[1]
>>> n == names
True
>>> c.solution.pool.delete(names[1])
>>> n = c.solution.pool.get_names()
>>> names.remove(names[1])
>>> n == names
True
>>> c.solution.pool.delete([names[1], 0])
>>> n = c.solution.pool.get_names()
>>> names.remove(names[1])
>>> del names[0]
>>> n == names
True
>>> c.solution.pool.delete()
>>> c.solution.pool.get_names()
[]
"""
def _delete(begin, end=None):
CPX_PROC.delsolnpoolsolns(self._env._e, self._cplex._lp,
begin, end)
delete_set_by_range(_delete, self._conv, self.get_num(), *args)
[docs]
def get_names(self, *args):
"""Returns the names of a set of solutions.
There are four forms by which solution.pool.get_names may be called.
solution.pool.get_names()
return the names of all solutions from the problem.
solution.pool.get_names(i)
i must be a solution index. Returns the name of row i.
solution.pool.get_names(s)
s must be a sequence of row indices. Returns the names of
the solutions with indices the members of s.
Equivalent to [solution.pool.get_names(i) for i in s]
solution.pool.get_names(begin, end)
begin and end must be solution indices. Returns the names of
the solutions with indices between begin and end, inclusive of
end. Equivalent to
solution.pool.get_names(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.parameters.randomseed.set(1)
>>> c.parameters.mip.limits.populate.set(10)
>>> c.populate_solution_pool()
>>> names = c.solution.pool.get_names()
>>> names[1] == c.solution.pool.get_names(1)
True
>>> [names[i] for i in [1,2]] == c.solution.pool.get_names([1,2])
True
>>> names[1:5] == c.solution.pool.get_names(1, 4)
True
"""
def getname(a):
return CPX_PROC.getsolnpoolsolnname(self._env._e,
self._cplex._lp,
a)
return apply_freeform_one_arg(
getname, self._conv, self.get_num(), args)
[docs]
def get_num_replaced(self):
"""Returns the number of solution pool members that have been replaced.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> c.solution.pool.get_num_replaced()
0
"""
return CPX_PROC.getsolnpoolnumreplaced(self._env._e, self._cplex._lp)
[docs]
def get_num(self):
"""Returns the number of solutions in the solution pool.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.parameters.randomseed.set(1)
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> num = c.solution.pool.get_num()
"""
return CPX_PROC.getsolnpoolnumsolns(self._env._e, self._cplex._lp)
[docs]
def write(self, filename, which=None):
"""Writes solutions to a file.
If no second argument is provided, all solutions are written
to file.
If a second argument is provided, it is the index of a
solution in the solution pool. Only that solution will be
written to file.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.parameters.randomseed.set(1)
>>> c.parameters.mip.limits.populate.set(10)
>>> c.populate_solution_pool()
>>> c.solution.pool.write("ind.sol",4)
"""
if which is None:
CPX_PROC.solwritesolnpoolall(self._env._e, self._cplex._lp,
filename)
else:
CPX_PROC.solwritesolnpool(self._env._e, self._cplex._lp,
which, filename)
[docs]
def get_quality_metrics(self, soln):
"""Returns an object containing measures of the quality of the
specified solution.
See `QualityMetrics`.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> c.parameters.randomseed.set(1)
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> qm = c.solution.pool.get_quality_metrics(0)
"""
if not isinstance(soln, int):
soln = self.get_indices(soln)
return QualityMetrics(self._cplex, soln)
[docs]
class AdvancedSolutionInterface(BaseInterface):
"""Advanced methods for accessing solution information.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> binvcol = c.solution.advanced.binvcol()
>>> binvrow = c.solution.advanced.binvrow()
>>> binvacol = c.solution.advanced.binvacol()
>>> binvarow = c.solution.advanced.binvarow()
>>> binvcol[0][24], binvcol[1][6]
(-0.215, 1.0)
>>> binvrow[24][0], binvrow[6][1]
(-0.215, 1.0)
>>> [x for i,x in enumerate(binvacol[0]) if i in range(0,3)], [x for i,x in enumerate(binvacol[1]) if i in range(0,3)]
([1.0, 0.0, 0.0], [0.0, 1.0, 0.0])
>>> [x for i,x in enumerate(binvarow[0]) if i in range(0,2)], [x for i,x in enumerate(binvarow[1]) if i in range(0,2)], [x for i,x in enumerate(binvarow[2]) if i in range(0,2)]
([1.0, 0.0], [0.0, 1.0], [0.0, 0.0])
>>> btran = c.solution.advanced.btran([1.0] * c.linear_constraints.get_num())
>>> bbtran = [x if x else 0.0 for i,x in enumerate(btran) if i in range(14,17)]
>>> [x if x else 0.0 for x in bbtran]
[0.0, 2.0, 1.0]
>>> ftran = c.solution.advanced.ftran([1.0] * c.linear_constraints.get_num())
>>> ftran[0]
2.891
"""
[docs]
def __init__(self, parent):
"""Creates a new AdvancedSolutionInterface.
The advanced solution interface is exposed by the top-level
`Cplex` class as Cplex.solution.advanced. This constructor is
not meant to be used externally.
"""
super().__init__(cplex=parent._cplex, advanced=True)
[docs]
def binvcol(self, *args):
"""Returns a set of columns of the inverted basis matrix.
Can be called by four forms.
solution.advanced.binvcol()
returns the inverted basis matrix as a list of columns.
solution.advanced.binvcol(i)
i must be a linear constraint name or index. Returns the
column of the inverted basis matrix associated with i.
solution.advanced.binvcol(s)
s must be a sequence of linear constraint names or indices.
Returns the columns of the inverted basis matrix associated
with the members of s. Equivalent to
[solution.advanced.binvcol(i) for i in s]
solution.advanced.binvcol(begin, end)
begin and end must be linear constraint indices or linear
constraint names. Returns the columns of the inverted basis
matrix associated with the linear constraints between begin
and end, inclusive of end. Equivalent to
solution.advanced.binvcol(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> binvcol = c.solution.advanced.binvcol()
>>> binvcol[0][24], binvcol[1][6]
(-0.215, 1.0)
"""
def inv(a):
return CPX_PROC.binvcol(self._env._e, self._cplex._lp, a)
return apply_freeform_one_arg(
inv, self._cplex.linear_constraints._conv,
self._cplex.linear_constraints.get_num(), args)
[docs]
def binvrow(self, *args):
"""Returns a set of rows of the inverted basis matrix.
Can be called by four forms.
solution.advanced.binvrow()
returns the inverted basis matrix as a list of rows.
solution.advanced.binvrow(i)
i must be a linear constraint name or index. Returns the
row of the inverted basis matrix associated with i.
solution.advanced.binvrow(s)
s must be a sequence of linear constraint names or indices.
Returns the rows of the inverted basis matrix associated
with the members of s. Equivalent to
[solution.advanced.binvrow(i) for i in s]
solution.advanced.binvrow(begin, end)
begin and end must be linear constraint indices or linear
constraint names. Returns the rows of the inverted basis matrix
associated with the linear constraints between begin and end,
inclusive of end. Equivalent to
solution.advanced.binvrow(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> binvrow = c.solution.advanced.binvrow()
>>> binvrow[24][0], binvrow[6][1]
(-0.215, 1.0)
"""
def inv(a):
return CPX_PROC.binvrow(self._env._e, self._cplex._lp, a)
return apply_freeform_one_arg(
inv, self._cplex.linear_constraints._conv,
self._cplex.linear_constraints.get_num(), args)
[docs]
def binvacol(self, *args):
"""Returns a set of columns of the tableau.
Can be called by four forms.
solution.advanced.binvacol()
returns the tableau as a list of columns.
solution.advanced.binvacol(i)
i must be a variable name or index. Returns the column of
the tableau associated with i.
solution.advanced.binvacol(s)
s must be a sequence of variable names or indices. Returns
the columns of the tableau associated with the members of s.
Equivalent to [solution.advanced.binvacol(i) for i in s]
solution.advanced.binvacol(begin, end)
begin and end must be variable indices or variable names.
Returns the columns of the tableau associated with the
variables between begin and end, inclusive of end. Equivalent
to solution.advanced.binvacol(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> binvacol = c.solution.advanced.binvacol()
>>> [x for i,x in enumerate(binvacol[0]) if i in range(0,3)], [x for i,x in enumerate(binvacol[1]) if i in range(0,3)]
([1.0, 0.0, 0.0], [0.0, 1.0, 0.0])
"""
def inv(a):
return CPX_PROC.binvacol(self._env._e, self._cplex._lp, a)
return apply_freeform_one_arg(
inv, self._cplex.variables._conv,
self._cplex.variables.get_num(), args)
[docs]
def binvarow(self, *args):
"""Returns a set of rows of the tableau.
Can be called by four forms.
solution.advanced.binvacol()
returns the tableau as a list of rows.
solution.advanced.binvacol(i)
i must be a linear constraint name or index. Returns the
row of the tableau associated with i.
solution.advanced.binvacol(s)
s must be a sequence of linear constraint names or indices.
Returns the rows of the tableau associated with the members
of s. Equivalent to [solution.advanced.binvacol(i)
for i in s]
solution.advanced.binvacol(begin, end)
begin and end must be linear constraint indices or variable
names. Returns the rows of the tableau associated with the
variables between begin and end, inclusive of end. Equivalent
to solution.advanced.binvacol(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> binvarow = c.solution.advanced.binvarow()
>>> [x for i,x in enumerate(binvarow[0]) if i in range(0,2)], [x for i,x in enumerate(binvarow[1]) if i in range(0,2)], [x for i,x in enumerate(binvarow[2]) if i in range(0,2)]
([1.0, 0.0], [0.0, 1.0], [0.0, 0.0])
"""
def inv(a):
return CPX_PROC.binvarow(self._env._e, self._cplex._lp, a)
return apply_freeform_one_arg(
inv, self._cplex.linear_constraints._conv,
self._cplex.linear_constraints.get_num(), args)
[docs]
def btran(self, y):
"""Performs a backward linear solve using the basis matrix.
Returns the solution to the linear system
x^T B = y^T
y must be a list of floats with length equal to the number of
linear constraints.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> btran = c.solution.advanced.btran([1.0] * c.linear_constraints.get_num())
>>> bbtran = [x if x else 0.0 for i,x in enumerate(btran) if i in range(14,17)]
>>> [x if x else 0.0 for x in bbtran]
[0.0, 2.0, 1.0]
"""
return CPX_PROC.btran(self._env._e, self._cplex._lp, y)
[docs]
def ftran(self, x):
"""Performs a linear solve using the basis matrix.
Returns the solution to the linear system
B x = y
y must be a list of floats with length equal to the number of
linear constraints.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> ftran = c.solution.advanced.ftran([1.0] * c.linear_constraints.get_num())
>>> ftran[0]
2.891
"""
return CPX_PROC.ftran(self._env._e, self._cplex._lp, x)
[docs]
def get_gradients(self, *args):
"""Returns information useful in post-solution analysis after an
LP has been solved and a basis is available.
See `CPXgetgrad <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/getgrad.html>`_ in the Callable Library Reference Manual
for more detail.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> grad = c.solution.advanced.get_gradients(1)
>>> grad.ind[1]
1
>>> grad.val[1]
1.0
"""
def getgrad(a):
return SparsePair(*CPX_PROC.getgrad(self._env._e, self._cplex._lp, a))
return apply_freeform_one_arg(
getgrad, self._cplex.variables._conv,
self._cplex.variables.get_num(), args)
[docs]
def get_linear_slacks_from_x(self, x):
"""Computes the slack values from the given solution x
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> slack = c.solution.advanced.get_linear_slacks_from_x(c.solution.get_values())
>>> abs(slack[3]) < 1e-6
True
"""
return CPX_PROC.slackfromx(self._env._e, self._cplex._lp, x)
[docs]
def get_quadratic_slacks_from_x(self, x):
"""Computes the slack values for quadratic constraints from the given solution x
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("qcp.lp")
>>> c.solve()
>>> qslack = c.solution.advanced.get_quadratic_slacks_from_x(c.solution.get_values())
>>> abs(qslack[0]) < 1e-6
True
"""
return CPX_PROC.qconstrslackfromx(self._env._e, self._cplex._lp, x)
[docs]
def get_linear_reduced_costs_from_pi(self, pi):
"""Computes the reduced costs from the given dual solution pi
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> reducedcost = c.solution.advanced.get_linear_reduced_costs_from_pi(
c.solution.get_dual_values())
>>> abs(reducedcost[0]) < 1e-6
True
"""
return CPX_PROC.djfrompi(self._env._e, self._cplex._lp, pi)
[docs]
def get_quadratic_reduced_costs_from_pi(self, pi, x):
"""Computes the reduced costs for QP from the given solution (pi, x)
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("qp.lp")
>>> c.solve()
>>> qreducedcost = c.solution.advanced.get_quadratic_reduced_costs_from_pi(
c.solution.get_dual_values(),
c.solution.get_values())
>>> abs(qreducedcost[0]) < 1e-6
True
"""
return CPX_PROC.qpdjfrompi(self._env._e, self._cplex._lp, pi, x)
[docs]
def get_Driebeek_penalties(self, basic_variables):
"""Returns values known as Driebeek penalties for a sequence of basic variables.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> c_stat, _ = c.solution.basis.get_basis()
>>> b = [i for i, v in enumerate(c_stat) if v == c.solution.basis.status.basic]
>>> penalties = c.solution.advanced.get_Driebeek_penalties(b)
>>> penalties[0]
(0.34477142857142856, 8.021494102228047)
"""
return unzip(CPX_PROC.mdleave(
self._env._e, self._cplex._lp,
self._cplex.variables._conv(basic_variables)))
[docs]
def get_quadratic_indefinite_certificate(self):
"""Compute a vector x that satisfies x'Qx < 0
Such a vector demonstrates that the matrix Q violates the
assumption of positive semi-definiteness, and can be an aid in
debugging a user's program if indefiniteness is an unexpected
outcome.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("qpindef.lp")
>>> x = c.solution.advanced.get_quadratic_indefinite_certificate()
>>> abs(-0.5547001 - x[1]) < 1e-6
True
"""
return CPX_PROC.qpindefcertificate(self._env._e, self._cplex._lp)
[docs]
def dual_farkas(self):
"""Returns Farkas proof of infeasibility for the active LP model after proven infeasibility.
See `CPXdualfarkas <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/dualfarkas.html>`_ in the Callable Library Reference
Manual for more detail.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> indices = c.linear_constraints.add(senses="L", rhs=[-1])
>>> indices = c.variables.add(lb=[1], ub=[2],columns=[[[0],[1]]])
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.dual)
>>> c.parameters.preprocessing.presolve.set(c.parameters.preprocessing.presolve.values.off)
>>> c.solve()
>>> y = c.solution.advanced.dual_farkas()
>>> y[1]
2.0
"""
return CPX_PROC.dualfarkas(self._env._e, self._cplex._lp)
[docs]
def get_diverging_index(self):
"""Returns the index of the diverging row or column
if the problem is not unbounded, get_diverging_index returns -1.
If the problem is unbounded, get_diverging_index returns the
index of the diverging variable in the augmented form of the
constraint matrix. In other words, if the diverging variable
is a structural variable, get_diverging_index returns its
index; if the diverging variable is a slack or ranged
variable, get_diverging_index returns the sum of the number of
structural variables and the index of the corresponding
constraint.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> indices = c.variables.add(obj=[1,1],lb=[1,-cplex.infinity],ub=[2,cplex.infinity])
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.primal)
>>> c.parameters.preprocessing.presolve.set(c.parameters.preprocessing.presolve.values.off)
>>> c.solve()
>>> idx = c.solution.advanced.get_diverging_index()
>>> idx
1
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> indices = c.variables.add(obj=[-1,-1],lb=[1,1],ub=[2,2])
>>> indices = c.linear_constraints.add(lin_expr = [[[0,1],[-1,-1]]], rhs=[-10], senses="L")
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.dual)
>>> c.parameters.preprocessing.presolve.set(c.parameters.preprocessing.presolve.values.off)
>>> c.solve()
>>> idx = c.solution.advanced.get_diverging_index()
>>> idx
2
"""
return CPX_PROC.getijdiv(self._env._e, self._cplex._lp)
[docs]
def get_ray(self):
"""Returns an unbounded direction, i.e., ray, if a LP model is unbounded
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("unblp.lp")
>>> c.parameters.preprocessing.presolve.set(c.parameters.preprocessing.presolve.values.off)
>>> c.solve()
>>> ray = c.solution.advanced.get_ray()
>>> ray[0]
-1.0
"""
return CPX_PROC.getray(self._env._e, self._cplex._lp)
[docs]
class SolutionMethod(ConstantClass):
"""Solution methods."""
none = _constants.CPX_ALG_NONE
primal = _constants.CPX_ALG_PRIMAL
dual = _constants.CPX_ALG_DUAL
barrier = _constants.CPX_ALG_BARRIER
feasopt = _constants.CPX_ALG_FEASOPT
MIP = _constants.CPX_ALG_MIP
pivot = _constants.CPX_ALG_PIVOT
pivot_in = _constants.CPX_ALG_PIVOTIN
pivot_out = _constants.CPX_ALG_PIVOTOUT
[docs]
class SolutionStatus(ConstantClass):
"""Solution status codes.
For documentation of each status code, see the reference manual
of the CPLEX Callable Library, especially the group
optim.cplex.callable.solutionstatus.
"""
unknown = 0 # There is no constant for this.
optimal = _constants.CPX_STAT_OPTIMAL
unbounded = _constants.CPX_STAT_UNBOUNDED
infeasible = _constants.CPX_STAT_INFEASIBLE
feasible = _constants.CPX_STAT_FEASIBLE
infeasible_or_unbounded = _constants.CPX_STAT_INForUNBD
optimal_infeasible = _constants.CPX_STAT_OPTIMAL_INFEAS
num_best = _constants.CPX_STAT_NUM_BEST
feasible_relaxed_sum = _constants.CPX_STAT_FEASIBLE_RELAXED_SUM
optimal_relaxed_sum = _constants.CPX_STAT_OPTIMAL_RELAXED_SUM
feasible_relaxed_inf = _constants.CPX_STAT_FEASIBLE_RELAXED_INF
optimal_relaxed_inf = _constants.CPX_STAT_OPTIMAL_RELAXED_INF
feasible_relaxed_quad = _constants.CPX_STAT_FEASIBLE_RELAXED_QUAD
optimal_relaxed_quad = _constants.CPX_STAT_OPTIMAL_RELAXED_QUAD
abort_obj_limit = _constants.CPX_STAT_ABORT_OBJ_LIM
abort_primal_obj_limit = _constants.CPX_STAT_ABORT_PRIM_OBJ_LIM
abort_dual_obj_limit = _constants.CPX_STAT_ABORT_DUAL_OBJ_LIM
first_order = _constants.CPX_STAT_FIRSTORDER
abort_iteration_limit = _constants.CPX_STAT_ABORT_IT_LIM
abort_time_limit = _constants.CPX_STAT_ABORT_TIME_LIM
abort_dettime_limit = _constants.CPX_STAT_ABORT_DETTIME_LIM
abort_user = _constants.CPX_STAT_ABORT_USER
optimal_face_unbounded = _constants.CPX_STAT_OPTIMAL_FACE_UNBOUNDED
conflict_feasible = _constants.CPX_STAT_CONFLICT_FEASIBLE
conflict_minimal = _constants.CPX_STAT_CONFLICT_MINIMAL
conflict_abort_contradiction = _constants.CPX_STAT_CONFLICT_ABORT_CONTRADICTION
conflict_abort_time_limit = _constants.CPX_STAT_CONFLICT_ABORT_TIME_LIM
conflict_abort_dettime_limit = _constants.CPX_STAT_CONFLICT_ABORT_DETTIME_LIM
conflict_abort_iteration_limit = _constants.CPX_STAT_CONFLICT_ABORT_IT_LIM
conflict_abort_node_limit = _constants.CPX_STAT_CONFLICT_ABORT_NODE_LIM
conflict_abort_obj_limit = _constants.CPX_STAT_CONFLICT_ABORT_OBJ_LIM
conflict_abort_memory_limit = _constants.CPX_STAT_CONFLICT_ABORT_MEM_LIM
conflict_abort_user = _constants.CPX_STAT_CONFLICT_ABORT_USER
relaxation_unbounded = _constants.CPXMIP_ABORT_RELAXATION_UNBOUNDED
abort_relaxed = _constants.CPXMIP_ABORT_RELAXED
optimal_tolerance = _constants.CPXMIP_OPTIMAL_TOL
solution_limit = _constants.CPXMIP_SOL_LIM
populate_solution_limit = _constants.CPXMIP_POPULATESOL_LIM
node_limit_feasible = _constants.CPXMIP_NODE_LIM_FEAS
node_limit_infeasible = _constants.CPXMIP_NODE_LIM_INFEAS
fail_feasible = _constants.CPXMIP_FAIL_FEAS
fail_infeasible = _constants.CPXMIP_FAIL_INFEAS
mem_limit_feasible = _constants.CPXMIP_MEM_LIM_FEAS
mem_limit_infeasible = _constants.CPXMIP_MEM_LIM_INFEAS
fail_feasible_no_tree = _constants.CPXMIP_FAIL_FEAS_NO_TREE
fail_infeasible_no_tree = _constants.CPXMIP_FAIL_INFEAS_NO_TREE
optimal_populated = _constants.CPXMIP_OPTIMAL_POPULATED
optimal_populated_tolerance = _constants.CPXMIP_OPTIMAL_POPULATED_TOL
benders_num_best = _constants.CPX_STAT_BENDERS_NUM_BEST
MIP_optimal = _constants.CPXMIP_OPTIMAL
MIP_infeasible = _constants.CPXMIP_INFEASIBLE
MIP_time_limit_feasible = _constants.CPXMIP_TIME_LIM_FEAS
MIP_time_limit_infeasible = _constants.CPXMIP_TIME_LIM_INFEAS
MIP_dettime_limit_feasible = _constants.CPXMIP_DETTIME_LIM_FEAS
MIP_dettime_limit_infeasible = _constants.CPXMIP_DETTIME_LIM_INFEAS
MIP_abort_feasible = _constants.CPXMIP_ABORT_FEAS
MIP_abort_infeasible = _constants.CPXMIP_ABORT_INFEAS
MIP_optimal_infeasible = _constants.CPXMIP_OPTIMAL_INFEAS
MIP_unbounded = _constants.CPXMIP_UNBOUNDED
MIP_infeasible_or_unbounded = _constants.CPXMIP_INForUNBD
MIP_feasible_relaxed_sum = _constants.CPXMIP_FEASIBLE_RELAXED_SUM
MIP_optimal_relaxed_sum = _constants.CPXMIP_OPTIMAL_RELAXED_SUM
MIP_feasible_relaxed_inf = _constants.CPXMIP_FEASIBLE_RELAXED_INF
MIP_optimal_relaxed_inf = _constants.CPXMIP_OPTIMAL_RELAXED_INF
MIP_feasible_relaxed_quad = _constants.CPXMIP_FEASIBLE_RELAXED_QUAD
MIP_optimal_relaxed_quad = _constants.CPXMIP_OPTIMAL_RELAXED_QUAD
MIP_feasible = _constants.CPXMIP_FEASIBLE
multiobj_optimal = _constants.CPX_STAT_MULTIOBJ_OPTIMAL
multiobj_infeasible = _constants.CPX_STAT_MULTIOBJ_INFEASIBLE
multiobj_inforunbd = _constants.CPX_STAT_MULTIOBJ_INForUNBD
multiobj_non_optimal = _constants.CPX_STAT_MULTIOBJ_NON_OPTIMAL
multiobj_stopped = _constants.CPX_STAT_MULTIOBJ_STOPPED
multiobj_unbounded = _constants.CPX_STAT_MULTIOBJ_UNBOUNDED
[docs]
class SolutionType(ConstantClass):
"""Solution types"""
none = _constants.CPX_NO_SOLN
basic = _constants.CPX_BASIC_SOLN
nonbasic = _constants.CPX_NONBASIC_SOLN
primal = _constants.CPX_PRIMAL_SOLN
[docs]
class SolutionInterface(BaseInterface):
"""Methods for querying the solution to an optimization problem."""
method = SolutionMethod()
"""See `SolutionMethod()` """
quality_metric = QualityMetric()
"""See `QualityMetric()` """
status = SolutionStatus()
"""See `SolutionStatus()` """
type = SolutionType()
"""See `SolutionType()` """
[docs]
def __init__(self, cplex):
"""Creates a new SolutionInterface.
The solution interface is exposed by the top-level `Cplex` class
as Cplex.solution. This constructor is not meant to be used
externally.
"""
super().__init__(cplex)
self.progress = ProgressInterface(self)
"""See `ProgressInterface()` """
self.infeasibility = InfeasibilityInterface(self)
"""See `InfeasibilityInterface()` """
self.MIP = MIPSolutionInterface(self)
"""See `MIPSolutionInterface()` """
self.basis = BasisInterface(self)
"""See `BasisInterface()` """
self.sensitivity = SensitivityInterface(self)
"""See `SensitivityInterface()` """
self.pool = SolnPoolInterface(self)
"""See `SolnPoolInterface()` """
self.advanced = AdvancedSolutionInterface(self)
"""See `AdvancedSolutionInterface()` """
self.multiobj = MultiObjSolnInterface(self)
"""See `MultiObjSolnInterface()` """
[docs]
def get_status(self):
"""Returns the status of the solution.
Returns an attribute of Cplex.solution.status.
For interpretations of the status codes, see the
reference manual of the CPLEX Callable Library,
especially the group optim.cplex.callable.solutionstatus
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> c.solution.get_status()
1
"""
return CPX_PROC.getstat(self._env._e, self._cplex._lp)
[docs]
def get_method(self):
"""Returns the method used to solve the problem.
Returns an attribute of Cplex.solution.method.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> c.solution.get_method()
2
"""
return CPX_PROC.getmethod(self._env._e, self._cplex._lp)
[docs]
def get_status_string(self, status_code=None):
"""Returns a string describing the status of the solution.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> c.solution.get_status_string()
'optimal'
"""
if status_code is None:
status_code = self.get_status()
return CPX_PROC.getstatstring(self._env._e, status_code)
[docs]
def get_objective_value(self):
"""Returns the value of the objective function.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.solve()
>>> c.solution.get_objective_value()
-202.5
"""
return CPX_PROC.getobjval(self._env._e, self._cplex._lp)
[docs]
def get_values(self, *args):
"""Returns the values of a set of variables at the solution.
Can be called by four forms.
solution.get_values()
return the values of all variables from the problem.
solution.get_values(i)
i must be a variable name or index. Returns the value of
the variable whose index or name is i.
solution.get_values(s)
s must be a sequence of variable names or indices. Returns
the values of the variables with indices the members of s.
Equivalent to [solution.get_values(i) for i in s]
solution.get_values(begin, end)
begin and end must be variable indices or variable names.
Returns the values of the variables with indices between begin
and end, inclusive of end. Equivalent to
solution.get_values(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> c.solution.get_values([0, 4, 5])
[25.5, 0.0, 80.0]
"""
def getx(a, b=self._cplex.variables.get_num() - 1):
return CPX_PROC.getx(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(
getx, self._cplex.variables._conv, args)
[docs]
def get_reduced_costs(self, *args):
"""Returns the reduced costs of a set of variables.
The values returned by this method are defined to be the dual
multipliers for bound constraints on the specified variables.
Can be called by four forms.
solution.get_reduced_costs()
return the reduced costs of all variables from the problem.
solution.get_reduced_costs(i)
i must be a variable name or index. Returns the reduced
cost of the variable whose index or name is i.
solution.get_reduced_costs(s)
s must be a sequence of variable names or indices. Returns
the reduced costs of the variables with indices the members
of s. Equivalent to [solution.get_reduced_costs(i) for i
in s]
solution.get_reduced_costs(begin, end)
begin and end must be variable indices or variable names.
Returns the reduced costs of the variables with indices between
begin and end, inclusive of end. Equivalent to
solution.get_reduced_costs(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> c.solution.get_reduced_costs([0, 4, 5])
[0.0, 10.0, 0.0]
"""
def getdj(a, b=self._cplex.variables.get_num() - 1):
return CPX_PROC.getdj(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(
getdj, self._cplex.variables._conv, args)
[docs]
def get_dual_values(self, *args):
"""Returns a set of dual values.
Note that the values returned by this function are not only
meaningful for linear programs. Also for second order cone
programs, they provide information about the dual solution.
Refer to the user manual to see how to use the values returned by
this function for second order cone programs.
Can be called by four forms.
solution.get_dual_values()
return all dual values from the problem.
solution.get_dual_values(i)
i must be a linear constraint name or index. Returns the
dual value associated with the linear constraint whose
index or name is i.
solution.get_dual_values(s)
s must be a sequence of linear constraint names or indices.
Returns the dual values associated with the linear
constraints with indices the members of s. Equivalent to
[solution.get_dual_values(i) for i in s]
solution.get_dual_values(begin, end)
begin and end must be linear constraint indices or linear
constraint names. Returns the dual values associated with the
linear constraints with indices between begin and end,
inclusive of end. Equivalent to
solution.get_dual_values(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> pi = c.solution.get_dual_values([0, 1])
>>> for i, j in zip(pi, [-0.628571, 0.0]):
... abs(i - j) < 1e-6
...
True
True
"""
def getpi(a, b=self._cplex.linear_constraints.get_num() - 1):
return CPX_PROC.getpi(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(
getpi, self._cplex.linear_constraints._conv, args)
[docs]
def get_quadratic_dualslack(self, *args):
"""Returns the dual slack for a quadratic constraint.
The function returns the dual slack vector of its arguments as a
SparsePair.
The function argument may be either the index or the name of a
quadratic constraint.
"""
def getqconstrdslack(q):
res = CPX_PROC.getqconstrdslack(self._env._e, self._cplex._lp, q)
if res:
return SparsePair(res[0], res[1])
return SparsePair()
return apply_freeform_one_arg(
getqconstrdslack,
self._cplex.quadratic_constraints._conv,
CPX_PROC.getnumqconstrs(self._env._e, self._cplex._lp),
args)
[docs]
def get_linear_slacks(self, *args):
"""Returns a set of linear slacks.
Can be called by four forms.
solution.get_linear_slacks()
return all linear slack values from the problem.
solution.get_linear_slacks(i)
i must be a linear constraint name or index. Returns the
slack values associated with the linear constraint whose
index or name is i.
solution.get_linear_slacks(s)
s must be a sequence of linear constraint names or indices.
Returns the slack values associated with the linear
constraints with indices the members of s. Equivalent to
[solution.get_linear_slacks(i) for i in s]
solution.get_linear_slacks(begin, end)
begin and end must be linear constraint indices or linear
constraint names. Returns the slack values associated with the
linear constraints with indices between begin and end,
inclusive of end. Equivalent to
solution.get_linear_slacks(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> abs(c.solution.get_linear_slacks(5)) < 1e-6
True
"""
def getslack(a, b=self._cplex.linear_constraints.get_num() - 1):
return CPX_PROC.getslack(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(
getslack, self._cplex.linear_constraints._conv, args)
[docs]
def get_indicator_slacks(self, *args):
"""Returns a set of indicator slacks.
Can be called by four forms.
solution.get_indicator_slacks()
return all indicator slack values from the problem.
solution.get_indicator_slacks(i)
i must be a indicator constraint name or index. Returns
the slack values associated with the indicator constraint
whose index or name is i.
solution.get_indicator_slacks(s)
s must be a sequence of indicator constraint names or
indices. Returns the slack values associated with the
indicator constraints with indices the members of s.
Equivalent to [solution.get_indicator_slacks(i) for i in s]
solution.get_indicator_slacks(begin, end)
begin and end must be indicator constraint indices or indicator
constraint names. Returns the slack values associated with the
indicator constraints with indices between begin and end,
inclusive of end. Equivalent to
solution.get_indicator_slacks(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("ind.lp")
>>> c.solve()
>>> c.solution.get_indicator_slacks([0, 18])
[1e+20, 0.0]
"""
def getindslack(a, b=self._cplex.indicator_constraints.get_num() - 1):
return CPX_PROC.getindconstrslack(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(
getindslack, self._cplex.indicator_constraints._conv,
args)
[docs]
def get_quadratic_slacks(self, *args):
"""Returns a set of quadratic slacks.
Can be called by four forms.
solution.get_quadratic_slacks()
return all quadratic slack values from the problem.
solution.get_quadratic_slacks(i)
i must be a quadratic constraint name or index. Returns
the slack values associated with the quadratic constraint
whose index or name is i.
solution.get_quadratic_slacks(s)
s must be a sequence of quadratic constraint names or
indices. Returns the slack values associated with the
quadratic constraints with indices the members of s.
Equivalent to [solution.get_quadratic_slacks(i) for i in s]
solution.get_quadratic_slacks(begin, end)
begin and end must be quadratic constraint indices or quadratic
constraint names. Returns the slack values associated with the
quadratic constraints with indices between begin and end,
inclusive of end. Equivalent to
solution.get_quadratic_slacks(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> c.read("qcp.lp")
>>> c.solve()
>>> slack = c.solution.get_quadratic_slacks(0)
>>> abs(slack) < 1e-6
True
"""
def getqslack(a, b=self._cplex.quadratic_constraints.get_num() - 1):
return CPX_PROC.getqconstrslack(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(
getqslack, self._cplex.quadratic_constraints._conv, args)
[docs]
def get_integer_quality(self, which):
"""Returns a measure of the quality of the solution.
The measure of the quality of a solution can be a single attribute of
solution.quality_metrics or a sequence of such
attributes.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> m = c.solution.quality_metric
>>> c.solution.get_integer_quality([m.max_x, m.max_dual_infeasibility])
[18, -1]
"""
if isinstance(which, int):
return CPX_PROC.getintquality(self._env._e, self._cplex._lp, which)
return [CPX_PROC.getintquality(self._env._e, self._cplex._lp, a)
for a in which]
[docs]
def get_float_quality(self, which):
"""Returns a measure of the quality of the solution.
The measure of the quality of a solution can be a single attribute of
solution.quality_metrics or a sequence of such attributes.
Note
This corresponds to the CPLEX callable library function
CPXgetdblquality.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> m = c.solution.quality_metric
>>> c.solution.get_float_quality([m.max_x, m.max_dual_infeasibility])
[500.0, 0.0]
"""
if isinstance(which, int):
return CPX_PROC.getdblquality(self._env._e, self._cplex._lp, which)
return [CPX_PROC.getdblquality(self._env._e, self._cplex._lp, a)
for a in which]
[docs]
def get_solution_type(self):
"""Returns the type of the solution.
Returns an attribute of Cplex.solution.type.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> c.solution.get_solution_type()
1
"""
return CPX_PROC.solninfo(self._env._e, self._cplex._lp)[1]
[docs]
def is_primal_feasible(self):
"""Returns whether or not the solution is known to be primal feasible.
Note
Returning False does not necessarily mean that the problem is
not primal feasible, only that it is not proved to be primal
feasible.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> c.solution.is_primal_feasible()
True
"""
return bool(CPX_PROC.solninfo(self._env._e, self._cplex._lp)[2])
[docs]
def is_dual_feasible(self):
"""Returns whether or not the solution is known to be dual feasible.
Note
Returning False does not necessarily mean that the problem is
not dual feasible, only that it is not proved to be dual
feasible.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> c.solution.is_dual_feasible()
True
"""
return bool(CPX_PROC.solninfo(self._env._e, self._cplex._lp)[3])
[docs]
def get_activity_levels(self, *args):
"""Returns the activity levels for set of linear constraints.
Can be called by four forms.
solution.get_activity_levels()
return the activity levels for all linear constraints from
the problem.
solution.get_activity_levels(i)
i must be a linear constraint name or index. Returns the
activity levels for the linear constraint whose index or
name is i.
solution.get_activity_levels(s)
s must be a sequence of linear constraint names or indices.
Returns the activity levels for the linear constraints with
indices the members of s. Equivalent to
[solution.get_activity_levels(i) for i in s]
solution.get_activity_levels(begin, end)
begin and end must be linear constraint indices or linear
constraint names. Returns the activity levels for the linear
constraints with indices between begin and end, inclusive of
end. Equivalent to
solution.get_activity_levels(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> c.solution.get_activity_levels([2, 3, 12])
[80.0, 0.0, 500.0]
"""
def getax(a, b=self._cplex.linear_constraints.get_num() - 1):
return CPX_PROC.getax(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(
getax, self._cplex.linear_constraints._conv, args)
[docs]
def get_quadratic_activity_levels(self, *args):
"""Returns the activity levels for set of quadratic constraints.
Can be called by four forms.
solution.get_quadratic_activity_levels()
return the activity levels for all quadratic constraints
from the problem.
solution.get_quadratic_activity_levels(i)
i must be a quadratic constraint name or index. Returns
the activity levels for the quadratic constraint whose
index or name is i.
solution.get_quadratic_activity_levels(s)
s must be a sequence of quadratic constraint names or
indices. Returns the activity levels for the quadratic
constraints with indices the members of s. Equivalent to
[solution.get_quadratic_activity_levels(i) for i in s]
solution.get_quadratic_activity_levels(begin, end)
begin and end must be quadratic constraint indices or quadratic
constraint names. Returns the activity levels for the quadratic
constraints with indices between begin and end, inclusive of
end. Equivalent to
solution.get_quadratic_activity_levels(range(begin, end + 1)).
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> c.read("qcp.lp")
>>> c.solve()
>>> xqxax = c.solution.get_quadratic_activity_levels()
>>> abs(xqxax[0] - 2.015616) < 1e-6
True
"""
def getxqxax(a, b=self._cplex.quadratic_constraints.get_num() - 1):
return CPX_PROC.getxqxax(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(
getxqxax, self._cplex.quadratic_constraints._conv, args)
[docs]
def get_quality_metrics(self):
"""Returns an object containing measures of the solution quality.
See `QualityMetrics`.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> qm = c.solution.get_quality_metrics()
"""
return QualityMetrics(self._cplex)
[docs]
def write(self, filename):
"""Writes the incumbent solution to a file.
See `CPXsolwrite <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/solwrite.html>`_ in the Callable Library Reference
Manual and also `InitialInterface.read_start()`.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> c.solve()
>>> c.solution.write("lpex.sol")
"""
CPX_PROC.solwrite(self._env._e, self._cplex._lp, filename)
[docs]
class PresolveStatus(ConstantClass):
"""Presolve status codes"""
no_reductions = 0
has_problem = 1
empty_problem = 2
[docs]
class PresolveMethod(ConstantClass):
"""Presolve solution methods"""
none = _constants.CPX_ALG_NONE
primal = _constants.CPX_ALG_PRIMAL
dual = _constants.CPX_ALG_DUAL
barrier = _constants.CPX_ALG_BARRIER
[docs]
class PresolveColStatus(ConstantClass):
"""Presolve variable status codes"""
lower_bound = _constants.CPX_PRECOL_LOW
upper_bound = _constants.CPX_PRECOL_UP
fixed = _constants.CPX_PRECOL_FIX
aggregated = _constants.CPX_PRECOL_AGG
other = _constants.CPX_PRECOL_OTHER
[docs]
class PresolveRowStatus(ConstantClass):
"""Presolve linear constraint status codes"""
reduced = _constants.CPX_PREROW_RED
aggregated = _constants.CPX_PREROW_AGG
other = _constants.CPX_PREROW_OTHER
[docs]
class PresolveInterface(BaseInterface):
"""Methods for dealing with the presolved problem."""
status = PresolveStatus()
"""See `PresolveStatus()` """
method = PresolveMethod()
"""See `PresolveMethod()` """
col_status = PresolveColStatus()
"""See `PresolveColStatus()` """
row_status = PresolveRowStatus()
"""See `PresolveRowStatus()` """
[docs]
def crush_x(self, x):
"""Projects a primal solution down to the presolved space.
x must be a list of floats with length equal to the number of
variables in the original problem. Returns a list of floats
with length equal to the number of variables in the presolved
problem.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.presolve.presolve(c.presolve.method.dual)
>>> c.presolve.crush_x([1.0] * 4)
[1.0, 1.0, 1.0]
"""
return CPX_PROC.crushx(self._env._e, self._cplex._lp, x)
[docs]
def crush_pi(self, pi):
"""Projects a dual solution down to the presolved space.
pi must be a list of floats with length equal to the number of
linear constraints in the original problem. Returns a list of
floats with length equal to the number of linear constraints
in the presolved problem.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.presolve.presolve(c.presolve.method.dual)
>>> c.presolve.crush_pi([1.0] * 4)
[1.0, 1.0, 1.0]
"""
return CPX_PROC.crushpi(self._env._e, self._cplex._lp, pi)
[docs]
def uncrush_x(self, pre_x):
"""Projects a primal presolved solution up to the original space.
x must be a list of floats with length equal to the number of
variables in the presolved problem. Returns a list of floats
with length equal to the number of variables in the original
problem.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.presolve.presolve(c.presolve.method.dual)
>>> c.presolve.uncrush_x([1.0] * 3)
[1.0, 1.0, 1.0, 0.0]
"""
return CPX_PROC.uncrushx(self._env._e, self._cplex._lp, pre_x)
[docs]
def uncrush_pi(self, pre_pi):
"""Projects a dual presolved solution up to the presolved space.
pi must be a list of floats with length equal to the number of
linear constraints in the presolved problem. Returns a list
of floats with length equal to the number of linear
constraints in the original problem.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.presolve.presolve(c.presolve.method.dual)
>>> c.presolve.uncrush_pi([1.0] * 3)
[1.0, 1.0, 1.0, 0.0]
"""
return CPX_PROC.uncrushpi(self._env._e, self._cplex._lp, pre_pi)
[docs]
def free(self):
"""Frees the presolved problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.presolve.presolve(c.presolve.method.dual)
>>> c.presolve.free()
"""
CPX_PROC.freepresolve(self._env._e, self._cplex._lp)
[docs]
def get_status(self):
"""Returns the status of presolve.
Returns an attribute of Cplex.presolve.status.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.presolve.presolve(c.presolve.method.dual)
>>> c.presolve.get_status()
1
"""
return CPX_PROC.getprestat_status(self._env._e, self._cplex._lp)
[docs]
def get_row_status(self):
"""Returns the status of the original linear constraints.
Returns a list of integers with length equal to the number of
linear constraints in the original problem. Each entry of
this list is an attribute of Cplex.presolve.row_status.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.presolve.presolve(c.presolve.method.dual)
>>> c.presolve.get_row_status()
[-3, 1, 2, -3]
"""
return CPX_PROC.getprestat_r(self._env._e, self._cplex._lp)
[docs]
def get_col_status(self):
"""Returns the status of the original variables.
Returns a list of integers with length equal to the number of
variables in the original problem. Each entry of this list
is an attribute of Cplex.presolve.col_status.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.presolve.presolve(c.presolve.method.dual)
>>> c.presolve.get_col_status()
[0, 1, 2, -5]
"""
return CPX_PROC.getprestat_c(self._env._e, self._cplex._lp)
[docs]
def get_presolved_row_status(self):
"""Returns the status of the presolved linear constraints.
Returns a list of integers with length equal to the number of
linear constraints in the presolved problem. -1 indicates
that the presolved linear constraint corresponds to more than
one linear constraint in the original problem. Otherwise the
value is the index of the corresponding linear constraint in
the original problem.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.presolve.presolve(c.presolve.method.dual)
>>> c.presolve.get_presolved_row_status()
[-1, 1, 2]
"""
return CPX_PROC.getprestat_or(self._env._e, self._cplex._lp)
[docs]
def get_presolved_col_status(self):
"""Returns the status of the presolved variables.
Returns a list of integers with length equal to the number of
variables in the presolved problem. -1 indicates that the
presolved variable corresponds to a linear combination of more
than one variable in the original problem. Otherwise the
value is the index of the corresponding variable in the
original problem.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.presolve.presolve(c.presolve.method.dual)
>>> c.presolve.get_presolved_col_status()
[0, 1, 2]
"""
return CPX_PROC.getprestat_oc(self._env._e, self._cplex._lp)
[docs]
def add_rows(self, lin_expr=None, senses="", rhs=None, names=None):
"""Adds linear constraints to the presolved problem.
presolve.add_rows accepts the keyword arguments lin_expr,
senses, rhs, and names.
If more than one argument is specified, all arguments must
have the same length.
lin_expr may be either a list of SparsePair instances or a
matrix in list-of-lists format.
Note
The entries of lin_expr must not contain duplicate indices.
If an entry of lin_expr references a variable more than
once, either by index, name, or a combination of index and
name, an exception will be raised.
senses must be either a list of single-character strings or a
string containing the types of the variables.
rhs is a list of floats, specifying the righthand side of
each linear constraint.
names is a list of strings.
The specified constraints are added to both the original
problem and the presolved problem.
"""
lin_expr, senses, rhs, names = init_list_args(
lin_expr, senses, rhs, names)
if not isinstance(senses, str):
senses = "".join(senses)
validate_arg_lengths(
[rhs, senses, names, lin_expr],
extra_msg=": lin_expr, senses, rhs, names"
)
if isinstance(lin_expr, list):
rmat = _HBMatrix(lin_expr)
CPX_PROC.preaddrows(self._env._e, self._cplex._lp, rhs, senses,
rmat.matbeg, rmat.matind, rmat.matval, names)
# TODO: We don't return an iterator here because there's no way to
# get indices of presolve rows from names.
[docs]
def set_objective(self, objective):
"""Sets the linear objective function of the presolved problem.
objective must be either a SparsePair instance or a list of
two lists, the first of which contains variable indices or
names, the second of which contains floats.
The objective function of both the original problem and the
presolved problem are changed.
"""
ind, val = unpack_pair(objective)
CPX_PROC.prechgobj(self._env._e, self._cplex._lp, ind, val)
[docs]
def presolve(self, method):
"""Solves the presolved problem.
method must be an attribute of Cplex.presolve.method.
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("example.mps")
>>> c.presolve.presolve(c.presolve.method.dual)
"""
CPX_PROC.presolve(self._env._e, self._cplex._lp, method)
[docs]
def write(self, filename):
"""Writes the presolved problem to a file."""
return CPX_PROC.preslvwrite(self._env._e, self._cplex._lp,
filename)
[docs]
class FeasoptConstraintType(ConstantClass):
"""Types of constraints"""
lower_bound = _constants.CPX_CON_LOWER_BOUND
upper_bound = _constants.CPX_CON_UPPER_BOUND
linear = _constants.CPX_CON_LINEAR
quadratic = _constants.CPX_CON_QUADRATIC
indicator = _constants.CPX_CON_INDICATOR
[docs]
class FeasoptInterface(BaseInterface):
"""Finds a minimal relaxation of the problem that is feasible.
This is a callable class. To find a feasible relaxation of a problem,
invoke the `__call__` method of this class.
"""
constraint_type = FeasoptConstraintType()
"""See `FeasoptConstraintType()` """
[docs]
def all_constraints(self):
"""Returns an object instructing feasopt to relax all constraints.
Calling Cplex.feasopt(Cplex.feasopt.all_constraints()) will
result in every constraint being relaxed independently with
equal weight.
See also the `__call__` method of this class.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.feasopt.all_constraints()
"""
gp = self.upper_bound_constraints()._gp
gp += self.lower_bound_constraints()._gp
gp += self.linear_constraints()._gp
gp += self.quadratic_constraints()._gp
gp += self.indicator_constraints()._gp
return _group(gp)
[docs]
def upper_bound_constraints(self, *args):
"""Returns an object instructing feasopt to relax all upper bounds.
If called with no arguments, every upper bound is assigned
weight 1.0.
If called with one or more arguments, every upper bound is
assigned a weight equal to the float passed in as the first
argument.
If additional arguments are specified, they determine a subset
of upper bounds to be relaxed. If one variable index or name
is specified, it is the only upper bound that can be relaxed.
If two variable indices or names are specified, then upper
bounds of all variables between the first and the second,
inclusive, can be relaxed. If a sequence of variable names or
indices is passed in, all of their upper bounds can be
relaxed.
See also the `__call__` method of this class.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.feasopt.upper_bound_constraints()
"""
return self._make_group(self.constraint_type.upper_bound, *args)
[docs]
def lower_bound_constraints(self, *args):
"""Returns an object instructing feasopt to relax all lower bounds.
If called with no arguments, every lower bound is assigned
weight 1.0.
If called with one or more arguments, every lower bound is
assigned a weight equal to the float passed in as the first
argument.
If additional arguments are specified, they determine a subset
of lower bounds to be relaxed. If one variable index or name
is specified, it is the only lower bound that can be relaxed.
If two variable indices or names are specified, then lower
bounds of all variables between the first and the second,
inclusive, can be relaxed. If a sequence of variable names or
indices is passed in, all of their lower bounds can be
relaxed.
See also the `__call__` method of this class.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.feasopt.lower_bound_constraints()
"""
return self._make_group(self.constraint_type.lower_bound, *args)
[docs]
def linear_constraints(self, *args):
"""Returns an object instructing feasopt to relax all linear constraints.
If called with no arguments, every linear constraint is
assigned weight 1.0.
If called with one or more arguments, every linear constraint
is assigned a weight equal to the float passed in as the first
argument.
If additional arguments are specified, they determine a subset
of linear constraints to be relaxed. If one linear constraint
index or name is specified, it is the only linear constraint
that can be relaxed. If two linear constraint indices or
names are specified, then all linear constraints between the
first and the second, inclusive, can be relaxed. If a sequence
of linear constraint names or indices is passed in, all of their
linear constraints can be relaxed.
See also the `__call__` method of this class.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.feasopt.linear_constraints()
"""
return self._make_group(self.constraint_type.linear, *args)
[docs]
def quadratic_constraints(self, *args):
"""Returns an object instructing feasopt to relax all quadratic constraints.
If called with no arguments, every quadratic constraint is
assigned weight 1.0.
If called with one or more arguments, every quadratic
constraint is assigned a weight equal to the float passed in
as the first argument.
If additional arguments are specified, they determine a subset
of quadratic constraints to be relaxed. If one quadratic
constraint index or name is specified, it is the only
quadratic constraint that can be relaxed. If two quadratic
constraint indices or names are specified, then all quadratic
constraints between the first and the second, inclusive, can be
relaxed. If a sequence of quadratic constraint names or indices
is passed in, all of their quadratic constraints can be relaxed.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.feasopt.quadratic_constraints()
"""
return self._make_group(self.constraint_type.quadratic, *args)
[docs]
def indicator_constraints(self, *args):
"""Returns an object instructing feasopt to relax all indicator constraints.
If called with no arguments, every indicator constraint is
assigned weight 1.0.
If called with one or more arguments, every indicator
constraint is assigned a weight equal to the float passed in
as the first argument.
If additional arguments are specified, they determine a subset
of indicator constraints to be relaxed. If one indicator
constraint index or name is specified, it is the only
indicator constraint that can be relaxed. If two indicator
constraint indices or names are specified, then all indicator
constraints between the first and the second, inclusive, can be
relaxed. If a sequence of indicator constraint names or indices
is passed in, all of their indicator constraints can be relaxed.
See also the `__call__` method of this class.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.feasopt.indicator_constraints()
"""
return self._make_group(self.constraint_type.indicator, *args)
[docs]
def __call__(self, *args):
"""Finds a minimal relaxation of the problem that is feasible.
This method can take arbitrarily many arguments. Either the
object returned by feasopt.all_constraints() or any combination
of constraint groups and objects returned by
`upper_bound_constraints()`, `lower_bound_constraints()`,
`linear_constraints()`, `quadratic_constraints()`, or
`indicator_constraints()` may be used to specify the constraints
to consider.
Constraint groups are sequences of length two, the first entry of
which is the preference for the group (a float), the second of
which is a sequence of pairs (type, id), where type is an
attribute of self.constraint_type and id is either an index or a
valid name for the type.
See `CPXfeasoptext <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/feasoptext.html>`_ in the Callable Library Reference
Manual.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("infeasible.lp")
>>> c.feasopt(c.feasopt.all_constraints())
>>> c.solution.get_objective_value()
2.0
>>> c.solution.get_values()
[3.0, 2.0, 3.0, 2.0]
"""
if not args:
raise WrongNumberOfArgumentsError(
"Requires at least one argument")
gpref, gbeg, ind, indt = [], [], [], []
args = list(args) # so we can call extend() below
for group in args:
if isinstance(group, _group):
args.extend(group._gp)
continue
gpref.append(group[0])
gbeg.append(len(ind))
for con in group[1]:
tran = self._getconvfunc(con[0])
indt.append(con[0])
ind.append(tran(con[1]))
self._cplex._setup_callbacks()
CPX_PROC.feasoptext(self._env._e, self._cplex._lp,
gpref, gbeg, ind, indt)
def _make_group(self, which, *args):
conv = self._getconvfunc(which)
max_num = self._getnum(which)
return make_group(conv, max_num, which, *args)
def _getinterface(self, which):
contype = self.constraint_type
switcher = {
contype.lower_bound: self._cplex.variables,
contype.upper_bound: self._cplex.variables,
contype.linear: self._cplex.linear_constraints,
contype.quadratic: self._cplex.quadratic_constraints,
contype.indicator: self._cplex.indicator_constraints
}
return switcher[which]
def _getnum(self, which):
interface = self._getinterface(which)
return interface.get_num()
def _getconvfunc(self, which):
interface = self._getinterface(which)
return interface._conv
[docs]
class ConflictStatus(ConstantClass):
"""Status codes returned by conflict.get"""
excluded = _constants.CPX_CONFLICT_EXCLUDED
possible_member = _constants.CPX_CONFLICT_POSSIBLE_MEMBER
member = _constants.CPX_CONFLICT_MEMBER
[docs]
class ConflictConstraintType(ConstantClass):
"""Types of constraints"""
lower_bound = _constants.CPX_CON_LOWER_BOUND
upper_bound = _constants.CPX_CON_UPPER_BOUND
linear = _constants.CPX_CON_LINEAR
quadratic = _constants.CPX_CON_QUADRATIC
indicator = _constants.CPX_CON_INDICATOR
SOS = _constants.CPX_CON_SOS
pwl = _constants.CPX_CON_PWL
[docs]
class ConflictInterface(BaseInterface):
"""Methods for identifying conflicts among constraints."""
group_status = ConflictStatus()
"""See `ConflictStatus()` """
constraint_type = ConflictConstraintType()
"""See `ConflictConstraintType()` """
[docs]
def __init__(self, cplex):
"""Creates a new ConflictInterface.
The conflict interface is exposed by the top-level `Cplex` class
as Cplex.conflict. This constructor is not meant to be used
externally.
"""
super().__init__(cplex)
[docs]
def all_constraints(self):
"""Returns an object instructing the conflict refiner to include
all constraints.
Calling
Cplex.conflict.refine(Cplex.conflict.all_constraints()) or
Cplex.conflict.refine_MIP_start(Cplex.conflict.all_constraints())
will result in every constraint being included in the search
for conflicts with equal preference.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.conflict.all_constraints()
"""
gp = self.upper_bound_constraints()._gp
gp += self.lower_bound_constraints()._gp
gp += self.linear_constraints()._gp
gp += self.quadratic_constraints()._gp
gp += self.SOS_constraints()._gp
gp += self.indicator_constraints()._gp
gp += self.pwl_constraints()._gp
return _group(gp)
[docs]
def upper_bound_constraints(self, *args):
"""Returns an object instructing the conflict refiner to include
all upper bounds.
If called with no arguments, every upper bound is assigned
weight 1.0.
If called with one or more arguments, every upper bound is
assigned a weight equal to the float passed in as the first
argument.
If additional arguments are specified, they determine a subset
of upper bounds to be included. If one variable index or name
is specified, it is the only upper bound that will be
included. If two variable indices or names are specified, then
upper bounds of all variables between the first and the
second, inclusive, will be included. If a sequence of
variable names or indices is passed in, all of their upper
bounds will be included.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.conflict.upper_bound_constraints()
"""
return self._make_group(self.constraint_type.upper_bound, *args)
[docs]
def lower_bound_constraints(self, *args):
"""Returns an object instructing the conflict refiner to include
all lower bounds.
If called with no arguments, every lower bound is assigned
weight 1.0.
If called with one or more arguments, every lower bound is
assigned a weight equal to the float passed in as the first
argument.
If additional arguments are specified, they determine a subset
of lower bounds to be included. If one variable index or name
is specified, it is the only lower bound that will be
included. If two variable indices or names are specified, then
lower bounds of all variables between the first and the
second, inclusive, will be included. If a sequence of
variable names or indices is passed in, all of their lower
bounds will be included.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.conflict.lower_bound_constraints()
"""
return self._make_group(self.constraint_type.lower_bound, *args)
[docs]
def linear_constraints(self, *args):
"""Returns an object instructing the conflict refiner to include
all linear constraints.
If called with no arguments, every linear constraint is
assigned weight 1.0.
If called with one or more arguments, every linear constraint
is assigned a weight equal to the float passed in as the first
argument.
If additional arguments are specified, they determine a subset
of linear constraints to be included. If one linear
constraint index or name is specified, it is the only linear
constraint that will be included. If two linear constraint
indices or names are specified, then all linear constraints
between the first and the second, inclusive, will be included.
If a sequence of linear constraint names or indices is passed
in, they will all be included.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.conflict.linear_constraints()
"""
return self._make_group(self.constraint_type.linear, *args)
[docs]
def quadratic_constraints(self, *args):
"""Returns an object instructing the conflict refiner to include
all quadratic constraints.
If called with no arguments, every quadratic constraint is
assigned weight 1.0.
If called with one or more arguments, every quadratic
constraint is assigned a weight equal to the float passed in
as the first argument.
If additional arguments are specified, they determine a subset
of quadratic constraints to be included. If one quadratic
constraint index or name is specified, it is the only
quadratic constraint that will be included. If two quadratic
constraint indices or names are specified, then all quadratic
constraints between the first and the second, inclusive, will
be included. If a sequence of quadratic constraint names or
indices is passed in, they will all be included.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.conflict.quadratic_constraints()
"""
return self._make_group(self.constraint_type.quadratic, *args)
[docs]
def indicator_constraints(self, *args):
"""Returns an object instructing the conflict refiner to include
all indicator constraints.
If called with no arguments, every indicator constraint is
assigned weight 1.0.
If called with one or more arguments, every indicator
constraint is assigned a weight equal to the float passed in
as the first argument.
If additional arguments are specified, they determine a subset
of indicator constraints to be included. If one indicator
constraint index or name is specified, it is the only
indicator constraint that will be included. If two indicator
constraint indices or names are specified, the all indicator
constraints between the first and the second, inclusive, will
be included. If a sequence of indicator constraint names or
indices is passed in, they will all be included.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.conflict.indicator_constraints()
"""
return self._make_group(self.constraint_type.indicator, *args)
[docs]
def pwl_constraints(self, *args):
"""Returns an object instructing the conflict refiner to include
all PWL constraints.
If called with no arguments, every PWL constraint is assigned
weight 1.0.
If called with one or more arguments, every PWL constraint is
assigned a weight equal to the float passed in as the first
argument.
If additional arguments are specified, they determine a subset
of PWL constraints to be included. If one PWL constraint index or
name is specified, it is the only PWL constraint that will be
included. If two PWL constraint indices or names are specified,
then all PWL constraints between the first and the second,
inclusive, will be included. If a sequence of PWL constraint
names or indices is passed in, they will all be included.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.conflict.pwl_constraints()
"""
return self._make_group(self.constraint_type.pwl, *args)
[docs]
def SOS_constraints(self, *args):
"""Returns an object instructing the conflict refiner to include
all SOS constraints.
If called with no arguments, every SOS constraint is assigned
weight 1.0.
If called with one or more arguments, every SOS constraint is
assigned a weight equal to the float passed in as the first
argument.
If additional arguments are specified, they determine a subset
of SOS constraints to be included. If one SOS constraint
index or name is specified, it is the only SOS constraint that
will be included. If two SOS constraint indices or names are
specified, then all SOS constraints between the first and the
second, inclusive, will be included. If a sequence of SOS
constraint names or indices is passed in, they will all be
included.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> group = c.conflict.SOS_constraints()
"""
return self._make_group(self.constraint_type.SOS, *args)
@staticmethod
def _expand_groups(args):
"""Expands group arguments passed to the refine methods
These should be either _group objects or tuples of length two
(the first entry of which is the preference for the group (a
float), the second of which is a tuple of pairs (type, id),
where type is an attribute of conflict.constraint_type and id is
either an index or a valid name for the type).
As _group objects can contain many tuples, this method makes
sure that the expanded order is maintained.
"""
groups = []
for arg in args:
try:
# Grab the tuple list out of any _group objects we encounter.
groups.extend(arg._gp)
except AttributeError:
# Otherwise, we assume these are tuples.
groups.append(arg)
return groups
def _separate_groups(self, args):
"""Separates group information into individual lists.
This, so they can be passed into the callable library in the
expected format.
"""
grppref, grpbeg, grpind, grptype = [], [], [], []
groups = self._expand_groups(args)
for group in groups:
pref, contpl = group
grppref.append(pref)
grpbeg.append(len(grpind))
for contype, conid in contpl:
tran = self._getconvfunc(contype)
grptype.append(contype)
grpind.append(tran(conid))
return grppref, grpbeg, grpind, grptype
def _compose_groups(self, grppref, grpbeg, grpind, grptype):
"""Convert individual lists of group information into group
format.
These are tuples of length two (the first entry of which is the
preference for the group (a float), the second of which is a
tuple of pairs (type, id), where type is an attribute of
conflict.constraint_type and id is either an index or a valid
name for the type).
"""
groups = []
grpbeglen = len(grpbeg)
for i in range(grpbeglen):
begin = grpbeg[i]
if i == grpbeglen - 1:
end = len(grpind)
else:
end = grpbeg[i + 1]
groups.append((grppref[i], tuple(zip(grptype[begin:end],
grpind[begin:end]))))
return groups
def _make_group(self, which, *args):
conv = self._getconvfunc(which)
max_num = self._getnum(which)
return make_group(conv, max_num, which, *args)
def _getinterface(self, which):
contype = self.constraint_type
switcher = {
contype.lower_bound: self._cplex.variables,
contype.upper_bound: self._cplex.variables,
contype.linear: self._cplex.linear_constraints,
contype.quadratic: self._cplex.quadratic_constraints,
contype.SOS: self._cplex.SOS,
contype.indicator: self._cplex.indicator_constraints,
contype.pwl: self._cplex.pwl_constraints
}
return switcher[which]
def _getnum(self, which):
interface = self._getinterface(which)
return interface.get_num()
def _getconvfunc(self, which):
interface = self._getinterface(which)
return interface._conv
[docs]
def refine_MIP_start(self, MIP_start, *args):
"""Identifies a minimal conflict among a set of constraints for a
given MIP start.
This method can take arbitrarily many arguments. The first
argument must be either a name or index of a MIP start.
Additional arguments are optional and can be the object returned
by `all_constraints()` or any combination of constraint groups
and objects returned by `upper_bound_constraints()`,
`lower_bound_constraints()`, `linear_constraints()`,
`quadratic_constraints()`, `indicator_constraints()`,
`pwl_constraints()`, or `SOS_constraints()` may be used to
specify the constraints to consider. If no additional arguments
are specified, then constraint groups are created automatically
as in the CPLEX interactive.
Constraint groups are sequences of length two, the first entry
of which is the preference for the group (a float), the second
of which is a sequence of pairs (type, id), where type is an
attribute of conflict.constraint_type and id is either an index
or a valid name for the type.
See `CPXrefinemipstartconflictext <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/mipapi/refinemipstartconflictext.html>`_ and in the Callable
Library Reference Manual.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> indices = c.variables.add([1], [0], [0], c.variables.type.binary)
>>> indices = c.variables.add([2], [0], [0], c.variables.type.binary)
>>> c.solve()
>>> indices = c.linear_constraints.add(
... lin_expr=[[[0, 1], [1.0, 1.0]]], senses="E", rhs=[2.0])
>>> c.conflict.refine_MIP_start(0, c.conflict.all_constraints())
>>> c.conflict.get()
[-1, -1, -1, -1, 3]
>>> c.conflict.group_status[3], c.conflict.group_status[-1]
('member', 'excluded')
>>> c.conflict.get_groups(0, 3)
[(1.0, ((2, 0),)), (1.0, ((2, 1),)), (1.0, ((1, 0),)), (1.0, ((1, 1),))]
"""
self._cplex._setup_callbacks()
mipstartindex = self._cplex.MIP_starts._conv(MIP_start)
if args:
grppref, grpbeg, grpind, grptype = self._separate_groups(args)
else:
grppref, grpbeg, grpind, grptype = None, None, None, None
CPX_PROC.refinemipstartconflictext(self._env._e, self._cplex._lp,
mipstartindex,
grppref, grpbeg, grpind, grptype)
[docs]
def refine(self, *args):
"""Identifies a minimal conflict among a set of constraints.
This method can take arbitrarily many arguments. Either the
object returned by `all_constraints()` or any combination of
constraint groups and objects returned by
`upper_bound_constraints()`, `lower_bound_constraints()`,
`linear_constraints()`, `quadratic_constraints()`,
`indicator_constraints()`, `pwl_constraints()`, or
`SOS_constraints()` may be used to specify the constraints to
consider. Alternatively, if no arguments are specified, then
constraint groups are created automatically as in the CPLEX
interactive.
Constraint groups are sequences of length two, the first entry
of which is the preference for the group (a float), the second
of which is a sequence of pairs (type, id), where type is an
attribute of conflict.constraint_type and id is either an index or
a valid name for the type.
See `CPXrefineconflictext <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/refineconflictext.html>`_ in the Callable Library
Reference Manual.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> c.read("infeasible.lp")
>>> c.conflict.refine(c.conflict.linear_constraints(),
... c.conflict.lower_bound_constraints())
>>> c.conflict.get()
[3, -1, 3, -1, -1, -1]
>>> c.conflict.group_status[3], c.conflict.group_status[-1]
('member', 'excluded')
>>> c.conflict.get_groups([0, 2])
[(1.0, ((3, 0),)), (1.0, ((1, 0),))]
"""
self._cplex._setup_callbacks()
if args:
grppref, grpbeg, grpind, grptype = self._separate_groups(args)
else:
grppref, grpbeg, grpind, grptype = None, None, None, None
CPX_PROC.refineconflictext(self._env._e, self._cplex._lp,
grppref, grpbeg, grpind, grptype)
[docs]
def get(self, *args):
"""Returns the status of a set of groups of constraints.
Can be called by four forms.
If called with no arguments, returns a list containing the
status of all constraint groups.
If called with one integer argument, returns the status of
that constraint group.
If called with two integer arguments, returns the status of
all constraint groups between the first and second argument,
inclusive.
If called with a sequence of integers as its argument, returns
the status of all constraint groups in the sequence.
The status codes are attributes of
Cplex.conflict.group_status.
See `CPXgetconflictext <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/getconflictext.html>`_ in the Callable Library Reference
Manual.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> c.read("infeasible.lp")
>>> c.conflict.refine(c.conflict.all_constraints())
>>> confstatus = c.conflict.get()
"""
def getconflict(a, b=self.get_num_groups() - 1):
return CPX_PROC.getconflictext(self._env._e, self._cplex._lp, a, b)
return apply_freeform_two_args(getconflict, None, args)
[docs]
def get_num_groups(self):
"""Returns the number of constraint groups used in the last call
to `refine()` or `refine_MIP_start()`.
See `CPXgetconflictnumgroups <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/getconflictnumgroups.html>`_ in the Callable Library
Reference Manual.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> c.read("infeasible.lp")
>>> c.conflict.refine(c.conflict.all_constraints())
>>> c.conflict.get_num_groups()
10
"""
return CPX_PROC.getconflictnumgroups(self._env._e, self._cplex._lp)
[docs]
def get_groups(self, *args):
"""Returns the groups of constraints used in the last call to
`refine()` or `refine_MIP_start()`.
Can be called by four forms.
If called with no arguments, returns a list containing all
constraint groups.
If called with one integer argument, returns that constraint
group.
If called with two integer arguments, returns all constraint
groups between the first and second argument, inclusive.
If called with a sequence of integers as its argument, returns
all constraint groups in the sequence.
Constraint groups are tuples of length two, the first entry of
which is the preference for the group (a float), the second of
which is a tuple of pairs (type, id), where type is an
attribute of conflict.constraint_type and id is either an index
or a valid name for the type.
See `CPXgetconflictgroups <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/getconflictgroups.html>`_ in the Callable Library
Reference Manual.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> c.read("infeasible.lp")
>>> c.conflict.refine(c.conflict.all_constraints())
>>> groups = c.conflict.get_groups()
"""
def getgroups(begin, end=self.get_num_groups() - 1):
(grppref, grpbeg, grpind,
grptype) = CPX_PROC.getconflictgroups(self._env._e,
self._cplex._lp,
begin, end)
return self._compose_groups(grppref, grpbeg, grpind, grptype)
return apply_freeform_two_args(getgroups, None, args)
[docs]
def write(self, filename):
"""Writes the conflict to a file.
See `CPXclpwrite <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/clpwrite.html>`_ in the Callable Library Reference
Manual.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> c.read("infeasible.lp")
>>> c.conflict.refine(c.conflict.all_constraints())
>>> c.conflict.write("conflict.clp")
"""
CPX_PROC.clpwrite(self._env._e, self._cplex._lp, filename)
[docs]
class PivotVarStatus(ConstantClass):
"""Use as input to pivoting methods."""
at_lower_bound = _constants.CPX_AT_LOWER
at_upper_bound = _constants.CPX_AT_UPPER
[docs]
class AdvancedCplexInterface(BaseInterface):
"""Advanced control of a Cplex object."""
no_variable = _constants.CPX_NO_VARIABLE
"""See `_constants.CPX_NO_VARIABLE` """
variable_status = PivotVarStatus()
"""See `PivotVarStatus()` """
[docs]
def delete_names(self):
"""Deletes all names from the problem and its objects."""
CPX_PROC.delnames(self._env._e, self._cplex._lp)
[docs]
def basic_presolve(self):
"""Performs bound strengthening and detects redundant rows.
Returns a tuple containing three lists: a list containing the
strengthened lower bounds, a list containing the strengthened
upper bounds, and a list containing the status of each row.
See `CPXbasicpresolve <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/basicpresolve.html>`_ in the Callable Library Reference
Manual.
Note
This method does not create a presolved problem.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> c.read("lpex.mps")
>>> redlb, redub, rstat = c.advanced.basic_presolve()
"""
return CPX_PROC.basicpresolve(self._env._e, self._cplex._lp)
[docs]
def pivot(self, enter, leave, status):
"""Pivots a variable into the basis.
enter is a name or index of a variable or linear constraint.
The index of a slack variable is specified by a negative
integer; -i - 1 refers to the slack associated with the ith
linear constraint. enter must not identify a basic variable.
leave is a name or index of a variable or linear constraint.
The index of a slack variable is specified by a negative
integer; -i - 1 refers to the slack associated with the ith
linear constraint. leave must identify either a basic
variable or a non-basic variable with both a lower and upper
bound to indicate that it is to move to its opposite bound.
leave may also be set to Cplex.advanced.no_variable to
instruct CPLEX to use a ratio test to determine the entering
variable.
Note
If a linear constraint has the same name as a column, it must
be specified by -index - 1, not by name.
status must be an attribute of Cplex.advanced.variable_status
specifying the nonbasic status to be assigned to the leaving
variable after the basis change.
"""
def conv(var):
try:
return self._cplex.variables._conv(var)
except CplexSolverError:
# Variable name not found, try linear constraints.
return -self._cplex.linear_constraints._conv(var) - 1
CPX_PROC.pivot(self._env._e, self._cplex._lp, conv(enter),
conv(leave), status)
[docs]
def pivot_slacks_in(self, which):
"""Forcibly pivots slack variables into the basis.
which may be either a single linear constraint index or name
or a sequence of linear constraint indices or names.
"""
x = listify(self._cplex.linear_constraints._conv(which))
CPX_PROC.pivotin(self._env._e, self._cplex._lp, x)
[docs]
def pivot_fixed_variables_out(self, which):
"""Forcibly pivots structural variables out of the basis.
which may be either a single variable index or name or a
sequence of variable indices or names.
"""
x = listify(self._cplex.variables._conv(which))
CPX_PROC.pivotout(self._env._e, self._cplex._lp, x)
[docs]
def strong_branching(self, variables, it_limit):
"""Performs strong branching.
variables is a sequence of names or indices of variables.
it_limit is an integer that specifies the number of iterations
allowed.
Returns a list of pairs (down_penalty, up_penalty) with the
same length as variables containing the penalties for
branching down or up, respectively, on each variable.
See `CPXstrongbranch <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/strongbranch.html>`_ in the Callable Library Reference
Manual for more detail.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> out = c.set_results_stream(None)
>>> out = c.set_log_stream(None)
>>> itlim = c.parameters.simplex.limits.iterations.get()
>>> c.read("example.mps")
>>> c.solve()
>>> vars = list(range(c.variables.get_num()))
>>> result = c.advanced.strong_branching(vars, itlim)
"""
def conv(var):
return self._cplex.variables._conv(var)
return unzip(CPX_PROC.strongbranch(
self._env._e, self._cplex._lp,
conv(variables), it_limit))
[docs]
def complete(self):
"""See `CPXcompletelp <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/completelp.html>`_ in the Callable Library Reference
Manual."""
CPX_PROC.completelp(self._env._e, self._cplex.lp)
[docs]
class BranchDirection(ConstantClass):
"""Constants defining branch directions"""
default = _constants.CPX_BRANCH_GLOBAL
down = _constants.CPX_BRANCH_DOWN
up = _constants.CPX_BRANCH_UP
[docs]
class OrderInterface(BaseInterface):
"""Methods for setting and querying a priority order for branching.
Example usage:
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(names = [str(i) for i in range(5)])
>>> c.variables.set_types(zip(list(range(5)), ["C","I","I","I","I"]))
>>> c.order.set([(1, 10, c.order.branch_direction.up), ('3', 5, c.order.branch_direction.down)])
>>> c.order.get()
[(1, 10, 1), (3, 5, -1)]
>>> c.order.get_variables()
[1, 3]
"""
branch_direction = BranchDirection()
"""See `BranchDirection()` """
[docs]
def get(self):
"""Returns a list of triples (variable, priority, direction)
representing the priority order for branching.
"""
return unzip(CPX_PROC.getorder(self._env._e, self._cplex._lp))
[docs]
def get_variables(self):
"""Returns the variables for which an order has been set."""
return CPX_PROC.getorder(self._env._e, self._cplex._lp)[0]
[docs]
def set(self, order):
"""Sets the priority order for branching.
order must be a list of triples (variable, priority, direction).
variable must be an index or name of a variable.
priority must be a nonnegative integer.
direction must be an attribute of order.branch_direction.
"""
ord = unzip(order)
ord[0] = self._cplex.variables._conv(ord[0])
CPX_PROC.copyorder(self._env._e, self._cplex._lp, *ord)
[docs]
def read(self, filename):
"""Reads a priority order from a file."""
CPX_PROC.readcopyorder(self._env._e, self._cplex._lp, filename)
[docs]
def write(self, filename):
"""Writes the priority order to a file."""
CPX_PROC.ordwrite(self._env._e, self._cplex._lp, filename)
[docs]
class InitialInterface(BaseInterface):
"""Methods to set starting information for an optimization algorithm
to solve continuous problems (LP, QP, QCP).
Note
Data passed to these methods cannot be queried immediately from
the methods in Cplex.solution. Those methods will return
data only after Cplex.solve() or Cplex.feasopt() has been called.
"""
status = BasisVarStatus()
"""See `BasisVarStatus()` """
[docs]
def set_start(self, col_status, row_status, col_primal, row_primal,
col_dual, row_dual):
"""Sets basis statuses, primal values, and dual values.
The arguments col_status, col_primal, and col_dual are lists
that either have length equal to the number of variables or
are empty. If col_status is empty, then row_status must also
be empty. If col_primal is empty, then row_primal must also
be empty.
The arguments row_status, row_primal, and row_dual are lists
that either have length equal to the number of linear
constraints or are empty. If row_status is empty, the
col_status must also be empty. If row_dual is empty, then
col_dual must also be empty.
Each entry of col_status and row_status must be an attribute of
Cplex.start.status.
Each entry of col_primal and row_primal must be a float
specifying the starting primal values for the columns and
rows, respectively.
Each entry of col_dual and row_dual must be a float
specifying the starting dual values for the columns and rows,
respectively.
Note
The starting information is ignored by the optimizers if the
parameter cplex.parameters.advance is set to
cplex.parameters.advance.values.none.
>>> import cplex
>>> c = cplex.Cplex()
>>> indices = c.variables.add(
... names=["v{0}".format(i) for i in range(5)]
... )
>>> indices = c.linear_constraints.add(
... names=["r{0}".format(i) for i in range(3)]
... )
>>> s = c.start.status
>>> c.start.set_start(
... [s.basic] * 3 + [s.at_lower_bound] * 2,
... [s.basic] + [s.at_upper_bound] * 2,
... [0.0] * 5,
... [1.0] * 3,
... [2.0] * 5,
... [3.0] * 3
... )
"""
CPX_PROC.copystart(self._env._e, self._cplex._lp, col_status,
row_status, col_primal, row_primal, col_dual,
row_dual)
[docs]
def read_start(self, filename):
"""Reads the starting information from a file.
See `CPXreadcopystartinfo <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/readcopystartinfo.html>`_ in the Callable Library
Reference Manual and also `SolutionInterface.write()`.
Example usage:
>>> import cplex
>>> c1 = cplex.Cplex()
>>> out = c1.set_results_stream(None)
>>> out = c1.set_log_stream(None)
>>> c1.read("lpex.mps")
>>> c1.solve()
>>> c1.solution.write("lpex.sol")
>>> c2 = cplex.Cplex()
>>> out = c2.set_results_stream(None)
>>> out = c2.set_log_stream(None)
>>> c2.read("lpex.mps")
>>> c2.start.read_start("lpex.sol")
"""
CPX_PROC.readcopystartinfo(self._env._e, self._cplex._lp, filename)
[docs]
def read_basis(self, filename):
"""Reads the starting basis from a file.
See `CPXreadcopybase <https://www.ibm.com/docs/en/SSSA5P_22.1.2/ilog.odms.cplex.help/refcallablelibrary/cpxapi/readcopybase.html>`_ in the Callable Library Reference
Manual and also `BasisInterface.write()`.
Example:
>>> import cplex
>>> c1 = cplex.Cplex()
>>> out = c1.set_results_stream(None)
>>> out = c1.set_log_stream(None)
>>> c1.read("lpex.mps")
>>> c1.solve()
>>> c1.solution.basis.write("lpex.bas")
>>> c2 = cplex.Cplex()
>>> out = c2.set_results_stream(None)
>>> out = c2.set_log_stream(None)
>>> c2.read("lpex.mps")
>>> c2.start.read_basis("lpex.bas")
"""
CPX_PROC.readcopybase(self._env._e, self._cplex._lp, filename)