Source code for cplex._internal._subinterfaces

# --------------------------------------------------------------------------
# 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_header(self): """Returns the basis header. Returns a pair (head, x), where head is a list of variable indices and x is a list of floats indicating the values of those variables. Indices of basic slacks are specified by -rowindex - 1. """ return CPX_PROC.getbhead(self._env._e, self._cplex._lp)
[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_formula(self, formula): """Crushes a linear formula down into the presolved space. formula may either be an instance of the SparsePair class or a sequence of length two, the first entry of which contains variable names or indices, the second entry of which contains the float values associated with those variables. Returns a (crushed_formula, offset) pair, where crushed_formula is a SparsePair object containing the crushed formula in terms of the presolved variables and offset is the value of the linear formula corresponding to variables that have been removed 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_formula(cplex.SparsePair(ind = [1, 2], val = [1.0] * 2)) (SparsePair(ind = [1, 2], val = [1.0, 1.0]), 0.0) """ ind, val = unpack_pair(formula) ret = CPX_PROC.crushform(self._env._e, self._cplex._lp, ind, val) return (SparsePair(ret[1], ret[2]), ret[0])
[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_formula(self, pre_formula): """Uncrushes a linear formula up from the presolved space. formula may either be an instance of the SparsePair class or a sequence of length two, the first entry of which contains variable names or indices, the second entry of which contains the float values associated with those variables. Returns a (formula, offset) pair, where formula is a SparsePair object containing the formula in terms of variables in the original problem and offset is the value of the linear formula corresponding to variables that have been removed 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.uncrush_formula(cplex.SparsePair(ind = [1, 2], val = [1.0] * 2)) (SparsePair(ind = [1, 2], val = [1.0, 1.0]), 0.0) """ ind, val = unpack_pair(pre_formula) ret = CPX_PROC.uncrushform(self._env._e, self._cplex._lp, ind, val) return (SparsePair(ret[1], ret[2]), ret[0])
[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)