diff --git a/CHANGELOG.md b/CHANGELOG.md index 514a8cad..7dd4af49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Fixed too strict getObjVal, getVal check ### Changed - Changed createSol to now have an option of initialising at the current LP solution +- Unified documentation style of scip.pxi to numpydocs ### Removed ## 5.1.1 - 2024-06-22 diff --git a/docs/api.rst b/docs/api.rst index 1aeae47b..ec750ddd 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,79 +1,86 @@ ############# -API reference +API Reference ############# This page provides an auto-generated summary of PySCIPOpt's API. +.. automodule:: pyscipopt + SCIP Model ========== -.. autosummary:: - :toctree: _autosummary - :recursive: +This is the main class of PySCIPOpt. Most functionality is accessible through functions +of this class. All functions that require the SCIP object belong to this class. + +.. toctree:: + :maxdepth: 1 - pyscipopt.Model + api/model SCIP Constraint =============== -.. autosummary:: - :toctree: _autosummary - :recursive: +This class wraps a SCIP constraint object. It contains functions that can retrieve basic information +that is entirely contained within the constraint object. + +.. toctree:: + :maxdepth: 1 - pyscipopt.Constraint + api/constraint SCIP Variable ============= -.. autosummary:: - :toctree: _autosummary - :recursive: +This class wraps a SCIP variable object. It contains functions that can retrieve basic information +that is entirely contained within the variable object. - pyscipopt.Variable +.. toctree:: + :maxdepth: 1 + + api/variable SCIP Row ======== -.. autosummary:: - :toctree: _autosummary - :recursive: +This class wraps a SCIP row object. It contains functions that can retrieve basic information +that is entirely contained within the row object. + +.. toctree:: + :maxdepth: 1 - pyscipopt.scip.Row + api/row SCIP Column =========== -.. autosummary:: - :toctree: _autosummary - :recursive: +This class wraps a SCIP column object. It contains functions that can retrieve basic information +that is entirely contained within the column object. + +.. toctree:: + :maxdepth: 1 - pyscipopt.scip.Column + api/column SCIP Node ========= -.. autosummary:: - :toctree: _autosummary - :recursive: +This class wraps a SCIP node object. It contains functions that can retrieve basic information +that is entirely contained within the node object. - pyscipopt.scip.Node +.. toctree:: + :maxdepth: 1 -SCIP Solution -============= - -.. autosummary:: - :toctree: _autosummary - :recursive: - - pyscipopt.scip.Solution + api/node SCIP Event -=========== +========== + +This class wraps a SCIP event object. It contains functions that can retrieve basic information +that is entirely contained within the event object. -.. autosummary:: - :toctree: _autosummary - :recursive: +.. toctree:: + :maxdepth: 1 - pyscipopt.scip.Event + api/event diff --git a/docs/api/column.rst b/docs/api/column.rst new file mode 100644 index 00000000..938e11cd --- /dev/null +++ b/docs/api/column.rst @@ -0,0 +1,6 @@ +########## +Column API +########## + +.. autoclass:: pyscipopt.scip.Column + :members: \ No newline at end of file diff --git a/docs/api/constraint.rst b/docs/api/constraint.rst new file mode 100644 index 00000000..ecddfde1 --- /dev/null +++ b/docs/api/constraint.rst @@ -0,0 +1,6 @@ +############## +Constraint API +############## + +.. autoclass:: pyscipopt.Constraint + :members: \ No newline at end of file diff --git a/docs/api/event.rst b/docs/api/event.rst new file mode 100644 index 00000000..b138a5ed --- /dev/null +++ b/docs/api/event.rst @@ -0,0 +1,6 @@ +########## +Event API +########## + +.. autoclass:: pyscipopt.scip.Event + :members: \ No newline at end of file diff --git a/docs/api/model.rst b/docs/api/model.rst new file mode 100644 index 00000000..ee1521a6 --- /dev/null +++ b/docs/api/model.rst @@ -0,0 +1,6 @@ +######### +Model API +######### + +.. autoclass:: pyscipopt.Model + :members: \ No newline at end of file diff --git a/docs/api/node.rst b/docs/api/node.rst new file mode 100644 index 00000000..548e79e0 --- /dev/null +++ b/docs/api/node.rst @@ -0,0 +1,6 @@ +######## +Node API +######## + +.. autoclass:: pyscipopt.scip.Node + :members: \ No newline at end of file diff --git a/docs/api/row.rst b/docs/api/row.rst new file mode 100644 index 00000000..0a8ac06f --- /dev/null +++ b/docs/api/row.rst @@ -0,0 +1,6 @@ +####### +Row API +####### + +.. autoclass:: pyscipopt.scip.Row + :members: \ No newline at end of file diff --git a/docs/api/variable.rst b/docs/api/variable.rst new file mode 100644 index 00000000..8e7bbdd0 --- /dev/null +++ b/docs/api/variable.rst @@ -0,0 +1,6 @@ +############ +Variable API +############ + +.. autoclass:: pyscipopt.Variable + :members: \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 0e3d665a..b69be497 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -82,8 +82,9 @@ # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] -autosummary_generate = True -autoclass_content = "class" +autosummary_generate = False +napoleon_numpy_docstring = True +napoleon_google_docstring = False pygments_style = "sphinx" diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 3854d295..a57e322e 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -300,10 +300,24 @@ def PY_SCIP_CALL(SCIP_RETCODE rc): raise Exception('SCIP: unknown return code!') cdef class Event: - """Base class holding a pointer to corresponding SCIP_EVENT""" + """Base class holding a pointer to corresponding SCIP_EVENT.""" @staticmethod cdef create(SCIP_EVENT* scip_event): + """ + Main method for creating an Event class. Is used instead of __init__. + + Parameters + ---------- + scip_event : SCIP_EVENT* + A pointer to the SCIP_EVENT + + Returns + ------- + event : Event + The Python representative of the SCIP_EVENT + + """ if scip_event == NULL: raise Warning("cannot create Event with SCIP_EVENT* == NULL") event = Event() @@ -311,17 +325,31 @@ cdef class Event: return event def getType(self): - """gets type of event""" + """ + Gets type of event. + + Returns + ------- + PY_SCIP_EVENTTYPE + + """ return SCIPeventGetType(self.event) def getName(self): - """gets name of event""" + """ + Gets name of event. + + Returns + ------- + str + + """ if not EventNames: self._getEventNames() return EventNames[self.getType()] def _getEventNames(self): - """gets event names""" + """Gets event names.""" for name in dir(PY_SCIP_EVENTTYPE): attr = getattr(PY_SCIP_EVENTTYPE, name) if isinstance(attr, int): @@ -334,25 +362,61 @@ cdef class Event: return self.getName() def getNewBound(self): - """gets new bound for a bound change event""" + """ + Gets new bound for a bound change event. + + Returns + ------- + float + + """ return SCIPeventGetNewbound(self.event) def getOldBound(self): - """gets old bound for a bound change event""" + """ + Gets old bound for a bound change event. + + Returns + ------- + float + + """ return SCIPeventGetOldbound(self.event) def getVar(self): - """gets variable for a variable event (var added, var deleted, var fixed, objective value or domain change, domain hole added or removed)""" + """ + Gets variable for a variable event (var added, var deleted, var fixed, + objective value or domain change, domain hole added or removed). + + Returns + ------- + Variable + + """ cdef SCIP_VAR* var = SCIPeventGetVar(self.event) return Variable.create(var) def getNode(self): - """gets node for a node or LP event""" + """ + Gets node for a node or LP event. + + Returns + ------- + Node + + """ cdef SCIP_NODE* node = SCIPeventGetNode(self.event) return Node.create(node) def getRow(self): - """gets row for a row event""" + """ + Gets row for a row event. + + Returns + ------- + Row + + """ cdef SCIP_ROW* row = SCIPeventGetRow(self.event) return Row.create(row) @@ -364,10 +428,24 @@ cdef class Event: and self.event == (other).event) cdef class Column: - """Base class holding a pointer to corresponding SCIP_COL""" + """Base class holding a pointer to corresponding SCIP_COL.""" @staticmethod cdef create(SCIP_COL* scipcol): + """ + Main method for creating a Column class. Is used instead of __init__. + + Parameters + ---------- + scipcol : SCIP_COL* + A pointer to the SCIP_COL + + Returns + ------- + col : Column + The Python representative of the SCIP_COL + + """ if scipcol == NULL: raise Warning("cannot create Column with SCIP_COL* == NULL") col = Column() @@ -375,11 +453,35 @@ cdef class Column: return col def getLPPos(self): - """gets position of column in current LP, or -1 if it is not in LP""" + """ + Gets position of column in current LP, or -1 if it is not in LP. + + Returns + ------- + int + + """ return SCIPcolGetLPPos(self.scip_col) def getBasisStatus(self): - """gets the basis status of a column in the LP solution, Note: returns basis status `zero` for columns not in the current SCIP LP""" + """ + Gets the basis status of a column in the LP solution + + Returns + ------- + str + Possible values are "lower", "basic", "upper", and "zero" + + Raises + ------ + Exception + If SCIP returns an unknown basis status + + Notes + ----- + Returns basis status "zero" for columns not in the current SCIP LP. + + """ cdef SCIP_BASESTAT stat = SCIPcolGetBasisStatus(self.scip_col) if stat == SCIP_BASESTAT_LOWER: return "lower" @@ -393,33 +495,82 @@ cdef class Column: raise Exception('SCIP returned unknown base status!') def isIntegral(self): - """returns whether the associated variable is of integral type (binary, integer, implicit integer)""" + """ + Returns whether the associated variable is of integral type (binary, integer, implicit integer). + + Returns + ------- + bool + + """ return SCIPcolIsIntegral(self.scip_col) def getVar(self): - """gets variable this column represents""" + """ + Gets variable this column represents. + + Returns + ------- + Variable + + """ cdef SCIP_VAR* var = SCIPcolGetVar(self.scip_col) return Variable.create(var) def getPrimsol(self): - """gets the primal LP solution of a column""" + """ + Gets the primal LP solution of a column. + + Returns + ------- + float + + """ return SCIPcolGetPrimsol(self.scip_col) def getLb(self): - """gets lower bound of column""" + """ + Gets lower bound of column. + + Returns + ------- + float + + """ return SCIPcolGetLb(self.scip_col) def getUb(self): - """gets upper bound of column""" + """ + Gets upper bound of column. + + Returns + ------- + float + + """ return SCIPcolGetUb(self.scip_col) def getObjCoeff(self): - """gets objective value coefficient of a column""" + """ + Gets objective value coefficient of a column. + + Returns + ------- + float + + """ return SCIPcolGetObj(self.scip_col) def getAge(self): - """Gets the age of the column, i.e., the total number of successive times a column was in the LP - and was 0.0 in the solution""" + """ + Gets the age of the column, i.e., the total number of successive times a column was in the LP + and was 0.0 in the solution. + + Returns + ------- + int + + """ return SCIPcolGetAge(self.scip_col) def __hash__(self): @@ -430,10 +581,24 @@ cdef class Column: and self.scip_col == (other).scip_col) cdef class Row: - """Base class holding a pointer to corresponding SCIP_ROW""" + """Base class holding a pointer to corresponding SCIP_ROW.""" @staticmethod cdef create(SCIP_ROW* sciprow): + """ + Main method for creating a Row class. Is used instead of __init__. + + Parameters + ---------- + sciprow : SCIP_ROW* + A pointer to the SCIP_ROW + + Returns + ------- + row : Row + The Python representative of the SCIP_ROW + + """ if sciprow == NULL: raise Warning("cannot create Row with SCIP_ROW* == NULL") row = Row() @@ -446,23 +611,68 @@ cdef class Row: return cname.decode('utf-8') def getLhs(self): - """returns the left hand side of row""" + """ + Returns the left hand side of row. + + Returns + ------- + float + + """ return SCIProwGetLhs(self.scip_row) def getRhs(self): - """returns the right hand side of row""" + """ + Returns the right hand side of row. + + Returns + ------- + float + + """ return SCIProwGetRhs(self.scip_row) def getConstant(self): - """gets constant shift of row""" + """ + Gets constant shift of row. + + Returns + ------- + float + + """ return SCIProwGetConstant(self.scip_row) def getLPPos(self): - """gets position of row in current LP, or -1 if it is not in LP""" + """ + Gets position of row in current LP, or -1 if it is not in LP. + + Returns + ------- + int + + """ return SCIProwGetLPPos(self.scip_row) def getBasisStatus(self): - """gets the basis status of a row in the LP solution, Note: returns basis status `basic` for rows not in the current SCIP LP""" + """ + Gets the basis status of a row in the LP solution. + + Returns + ------- + str + Possible values are "lower", "basic", and "upper" + + Raises + ------ + Exception + If SCIP returns an unknown or "zero" basis status + + Notes + ----- + Returns basis status "basic" for rows not in the current SCIP LP. + + """ cdef SCIP_BASESTAT stat = SCIProwGetBasisStatus(self.scip_row) if stat == SCIP_BASESTAT_LOWER: return "lower" @@ -477,58 +687,150 @@ cdef class Row: raise Exception('SCIP returned unknown base status!') def isIntegral(self): - """returns TRUE iff the activity of the row (without the row's constant) is always integral in a feasible solution """ + """ + Returns TRUE iff the activity of the row (without the row's constant) + is always integral in a feasible solution. + + Returns + ------- + bool + + """ return SCIProwIsIntegral(self.scip_row) def isLocal(self): - """returns TRUE iff the row is only valid locally """ + """ + Returns TRUE iff the row is only valid locally. + + Returns + ------- + bool + + """ return SCIProwIsLocal(self.scip_row) def isModifiable(self): - """returns TRUE iff row is modifiable during node processing (subject to column generation) """ + """ + Returns TRUE iff row is modifiable during node processing (subject to column generation). + + Returns + ------- + bool + + """ return SCIProwIsModifiable(self.scip_row) def isRemovable(self): - """returns TRUE iff row is removable from the LP (due to aging or cleanup)""" + """ + Returns TRUE iff row is removable from the LP (due to aging or cleanup). + + Returns + ------- + bool + + """ return SCIProwIsRemovable(self.scip_row) def isInGlobalCutpool(self): - """return TRUE iff row is a member of the global cut pool""" + """ + Return TRUE iff row is a member of the global cut pool. + + Returns + ------- + bool + + """ return SCIProwIsInGlobalCutpool(self.scip_row) def getOrigintype(self): - """returns type of origin that created the row""" + """ + Returns type of origin that created the row. + + Returns + ------- + PY_SCIP_ROWORIGINTYPE + + """ return SCIProwGetOrigintype(self.scip_row) def getConsOriginConshdlrtype(self): - """returns type of constraint handler that created the row""" + """ + Returns type of constraint handler that created the row. + + Returns + ------- + str + + """ cdef SCIP_CONS* scip_con = SCIProwGetOriginCons(self.scip_row) return bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(scip_con))).decode('UTF-8') def getNNonz(self): - """get number of nonzero entries in row vector""" + """ + Get number of nonzero entries in row vector. + + Returns + ------- + int + + """ return SCIProwGetNNonz(self.scip_row) def getNLPNonz(self): - """get number of nonzero entries in row vector that correspond to columns currently in the SCIP LP""" + """ + Get number of nonzero entries in row vector that correspond to columns currently in the SCIP LP. + + Returns + ------- + int + + """ return SCIProwGetNLPNonz(self.scip_row) def getCols(self): - """gets list with columns of nonzero entries""" + """ + Gets list with columns of nonzero entries + + Returns + ------- + list of Column + + """ cdef SCIP_COL** cols = SCIProwGetCols(self.scip_row) return [Column.create(cols[i]) for i in range(self.getNNonz())] def getVals(self): - """gets list with coefficients of nonzero entries""" + """ + Gets list with coefficients of nonzero entries. + + Returns + ------- + list of int + + """ cdef SCIP_Real* vals = SCIProwGetVals(self.scip_row) return [vals[i] for i in range(self.getNNonz())] def getAge(self): - """Gets the age of the row. (The consecutive times the row has been non-active in the LP)""" + """ + Gets the age of the row. (The consecutive times the row has been non-active in the LP). + + Returns + ------- + int + + """ return SCIProwGetAge(self.scip_row) def getNorm(self): - """gets Euclidean norm of row vector """ + """ + Gets Euclidean norm of row vector. + + Returns + ------- + float + + """ return SCIProwGetNorm(self.scip_row) def __hash__(self): @@ -539,10 +841,24 @@ cdef class Row: and self.scip_row == (other).scip_row) cdef class NLRow: - """Base class holding a pointer to corresponding SCIP_NLROW""" + """Base class holding a pointer to corresponding SCIP_NLROW.""" @staticmethod cdef create(SCIP_NLROW* scipnlrow): + """ + Main method for creating a NLRow class. Is used instead of __init__. + + Parameters + ---------- + scipnlrow : SCIP_NLROW* + A pointer to the SCIP_NLROW + + Returns + ------- + nlrow : NLRow + The Python representative of the SCIP_NLROW + + """ if scipnlrow == NULL: raise Warning("cannot create NLRow with SCIP_NLROW* == NULL") nlrow = NLRow() @@ -555,26 +871,61 @@ cdef class NLRow: return cname.decode('utf-8') def getConstant(self): - """returns the constant of a nonlinear row""" + """ + Returns the constant of a nonlinear row. + + Returns + ------- + float + + """ return SCIPnlrowGetConstant(self.scip_nlrow) def getLinearTerms(self): - """returns a list of tuples (var, coef) representing the linear part of a nonlinear row""" + """ + Returns a list of tuples (var, coef) representing the linear part of a nonlinear row. + + Returns + ------- + list of tuple + + """ cdef SCIP_VAR** linvars = SCIPnlrowGetLinearVars(self.scip_nlrow) cdef SCIP_Real* lincoefs = SCIPnlrowGetLinearCoefs(self.scip_nlrow) cdef int nlinvars = SCIPnlrowGetNLinearVars(self.scip_nlrow) return [(Variable.create(linvars[i]), lincoefs[i]) for i in range(nlinvars)] def getLhs(self): - """returns the left hand side of a nonlinear row""" + """ + Returns the left hand side of a nonlinear row. + + Returns + ------- + float + + """ return SCIPnlrowGetLhs(self.scip_nlrow) def getRhs(self): - """returns the right hand side of a nonlinear row""" + """ + Returns the right hand side of a nonlinear row. + + Returns + ------- + float + + """ return SCIPnlrowGetRhs(self.scip_nlrow) def getDualsol(self): - """gets the dual NLP solution of a nonlinear row""" + """ + Gets the dual NLP solution of a nonlinear row. + + Returns + ------- + float + + """ return SCIPnlrowGetDualsol(self.scip_nlrow) def __hash__(self): @@ -585,7 +936,7 @@ cdef class NLRow: and self.scip_nlrow == (other).scip_nlrow) cdef class Solution: - """Base class holding a pointer to corresponding SCIP_SOL""" + """Base class holding a pointer to corresponding SCIP_SOL.""" # We are raising an error here to avoid creating a solution without an associated model. See Issue #625 def __init__(self, raise_error = False): @@ -594,6 +945,23 @@ cdef class Solution: @staticmethod cdef create(SCIP* scip, SCIP_SOL* scip_sol): + """ + Main method for creating a Solution class. Please use createSol method of the Model class + when wanting to create a Solution as a user. + + Parameters + ---------- + scip : SCIP* + A pointer to the SCIP object + scip_sol : SCIP_SOL* + A pointer to the SCIP_SOL + + Returns + ------- + sol : Solution + The Python representative of the SCIP_SOL + + """ if scip == NULL: raise Warning("cannot create Solution with SCIP* == NULL") sol = Solution(True) @@ -647,6 +1015,20 @@ cdef class BoundChange: @staticmethod cdef create(SCIP_BOUNDCHG* scip_boundchg): + """ + Main method for creating a BoundChange class. Is used instead of __init__. + + Parameters + ---------- + scip_boundchg : SCIP_BOUNDCHG* + A pointer to the SCIP_BOUNDCHG + + Returns + ------- + boundchg : BoundChange + The Python representative of the SCIP_BOUNDCHG + + """ if scip_boundchg == NULL: raise Warning("cannot create BoundChange with SCIP_BOUNDCHG* == NULL") boundchg = BoundChange() @@ -654,23 +1036,60 @@ cdef class BoundChange: return boundchg def getNewBound(self): - """Returns the new value of the bound in the bound change.""" + """ + Returns the new value of the bound in the bound change. + + Returns + ------- + float + + """ return SCIPboundchgGetNewbound(self.scip_boundchg) def getVar(self): - """Returns the variable of the bound change.""" + """ + Returns the variable of the bound change. + + Returns + ------- + Variable + + """ return Variable.create(SCIPboundchgGetVar(self.scip_boundchg)) def getBoundchgtype(self): - """Returns the bound change type of the bound change.""" + """ + Returns the bound change type of the bound change. + + Returns + ------- + int + (0 = branching, 1 = consinfer, 2 = propinfer) + + """ return SCIPboundchgGetBoundchgtype(self.scip_boundchg) def getBoundtype(self): - """Returns the bound type of the bound change.""" + """ + Returns the bound type of the bound change. + + Returns + ------- + int + (0 = lower, 1 = upper) + + """ return SCIPboundchgGetBoundtype(self.scip_boundchg) def isRedundant(self): - """Returns whether the bound change is redundant due to a more global bound that is at least as strong.""" + """ + Returns whether the bound change is redundant due to a more global bound that is at least as strong. + + Returns + ------- + bool + + """ return SCIPboundchgIsRedundant(self.scip_boundchg) def __repr__(self): @@ -683,6 +1102,20 @@ cdef class DomainChanges: @staticmethod cdef create(SCIP_DOMCHG* scip_domchg): + """ + Main method for creating a DomainChanges class. Is used instead of __init__. + + Parameters + ---------- + scip_domchg : SCIP_DOMCHG* + A pointer to the SCIP_DOMCHG + + Returns + ------- + domchg : DomainChanges + The Python representative of the SCIP_DOMCHG + + """ if scip_domchg == NULL: raise Warning("cannot create DomainChanges with SCIP_DOMCHG* == NULL") domchg = DomainChanges() @@ -690,7 +1123,14 @@ cdef class DomainChanges: return domchg def getBoundchgs(self): - """Returns the bound changes in the domain change.""" + """ + Returns the bound changes in the domain change. + + Returns + ------- + list of BoundChange + + """ nboundchgs = SCIPdomchgGetNBoundchgs(self.scip_domchg) return [BoundChange.create(SCIPdomchgGetBoundchg(self.scip_domchg, i)) for i in range(nboundchgs)] @@ -700,6 +1140,20 @@ cdef class Node: @staticmethod cdef create(SCIP_NODE* scipnode): + """ + Main method for creating a Node class. Is used instead of __init__. + + Parameters + ---------- + scipnode : SCIP_NODE* + A pointer to the SCIP_NODE + + Returns + ------- + node : Node + The Python representative of the SCIP_NODE + + """ if scipnode == NULL: return None node = Node() @@ -707,31 +1161,80 @@ cdef class Node: return node def getParent(self): - """Retrieve parent node (or None if the node has no parent node).""" + """ + Retrieve parent node (or None if the node has no parent node). + + Returns + ------- + Node + + """ return Node.create(SCIPnodeGetParent(self.scip_node)) def getNumber(self): - """Retrieve number of node.""" + """ + Retrieve number of node. + + Returns + ------- + int + + """ return SCIPnodeGetNumber(self.scip_node) def getDepth(self): - """Retrieve depth of node.""" + """ + Retrieve depth of node. + + Returns + ------- + int + + """ return SCIPnodeGetDepth(self.scip_node) def getType(self): - """Retrieve type of node.""" + """ + Retrieve type of node. + + Returns + ------- + PY_SCIP_NODETYPE + + """ return SCIPnodeGetType(self.scip_node) def getLowerbound(self): - """Retrieve lower bound of node.""" + """ + Retrieve lower bound of node. + + Returns + ------- + float + + """ return SCIPnodeGetLowerbound(self.scip_node) def getEstimate(self): - """Retrieve the estimated value of the best feasible solution in subtree of the node""" + """ + Retrieve the estimated value of the best feasible solution in subtree of the node. + + Returns + ------- + float + + """ return SCIPnodeGetEstimate(self.scip_node) def getAddedConss(self): - """Retrieve all constraints added at this node.""" + """ + Retrieve all constraints added at this node. + + Returns + ------- + list of Constraint + + """ cdef int addedconsssize = SCIPnodeGetNAddedConss(self.scip_node) if addedconsssize == 0: return [] @@ -744,19 +1247,47 @@ cdef class Node: return constraints def getNAddedConss(self): - """Retrieve number of added constraints at this node""" + """ + Retrieve number of added constraints at this node. + + Returns + ------- + int + + """ return SCIPnodeGetNAddedConss(self.scip_node) def isActive(self): - """Is the node in the path to the current node?""" + """ + Is the node in the path to the current node? + + Returns + ------- + bool + + """ return SCIPnodeIsActive(self.scip_node) def isPropagatedAgain(self): - """Is the node marked to be propagated again?""" + """ + Is the node marked to be propagated again? + + Returns + ------- + bool + + """ return SCIPnodeIsPropagatedAgain(self.scip_node) def getNParentBranchings(self): - """Retrieve the number of variable branchings that were performed in the parent node to create this node.""" + """ + Retrieve the number of variable branchings that were performed in the parent node to create this node. + + Returns + ------- + int + + """ cdef SCIP_VAR* dummy_branchvars cdef SCIP_Real dummy_branchbounds cdef SCIP_BOUNDTYPE dummy_boundtypes @@ -770,7 +1301,16 @@ cdef class Node: return nbranchvars def getParentBranchings(self): - """Retrieve the set of variable branchings that were performed in the parent node to create this node.""" + """ + Retrieve the set of variable branchings that were performed in the parent node to create this node. + + Returns + ------- + list of Variable + list of float + list of int + + """ cdef int nbranchvars = self.getNParentBranchings() if nbranchvars == 0: return None @@ -792,7 +1332,16 @@ cdef class Node: return py_variables, py_branchbounds, py_boundtypes def getNDomchg(self): - """Retrieve the number of bound changes due to branching, constraint propagation, and propagation.""" + """ + Retrieve the number of bound changes due to branching, constraint propagation, and propagation. + + Returns + ------- + nbranchings : int + nconsprop : int + nprop : int + + """ cdef int nbranchings cdef int nconsprop cdef int nprop @@ -800,7 +1349,14 @@ cdef class Node: return nbranchings, nconsprop, nprop def getDomchg(self): - """Retrieve domain changes for this node.""" + """ + Retrieve domain changes for this node. + + Returns + ------- + DomainChanges + + """ cdef SCIP_DOMCHG* domchg = SCIPnodeGetDomchg(self.scip_node) if domchg == NULL: return None @@ -818,6 +1374,20 @@ cdef class Variable(Expr): @staticmethod cdef create(SCIP_VAR* scipvar): + """ + Main method for creating a Variable class. Is used instead of __init__. + + Parameters + ---------- + scipvar : SCIP_VAR* + A pointer to the SCIP_VAR + + Returns + ------- + var : Variable + The Python representative of the SCIP_VAR + + """ if scipvar == NULL: raise Warning("cannot create Variable with SCIP_VAR* == NULL") var = Variable() @@ -838,7 +1408,15 @@ cdef class Variable(Expr): return self.name def vtype(self): - """Retrieve the variables type (BINARY, INTEGER, IMPLINT or CONTINUOUS)""" + """ + Retrieve the variables type (BINARY, INTEGER, IMPLINT or CONTINUOUS) + + Returns + ------- + str + "BINARY", "INTEGER", "CONTINUOUS", or "IMPLINT" + + """ vartype = SCIPvarGetType(self.scip_var) if vartype == SCIP_VARTYPE_BINARY: return "BINARY" @@ -850,64 +1428,163 @@ cdef class Variable(Expr): return "IMPLINT" def isOriginal(self): - """Retrieve whether the variable belongs to the original problem""" + """ + Retrieve whether the variable belongs to the original problem + + Returns + ------- + bool + + """ return SCIPvarIsOriginal(self.scip_var) def isInLP(self): - """Retrieve whether the variable is a COLUMN variable that is member of the current LP""" + """ + Retrieve whether the variable is a COLUMN variable that is member of the current LP. + + Returns + ------- + bool + + """ return SCIPvarIsInLP(self.scip_var) def getIndex(self): - """Retrieve the unique index of the variable.""" + """ + Retrieve the unique index of the variable. + + Returns + ------- + int + + """ return SCIPvarGetIndex(self.scip_var) def getCol(self): - """Retrieve column of COLUMN variable""" + """ + Retrieve column of COLUMN variable. + + Returns + ------- + Column + + """ cdef SCIP_COL* scip_col scip_col = SCIPvarGetCol(self.scip_var) return Column.create(scip_col) def getLbOriginal(self): - """Retrieve original lower bound of variable""" + """ + Retrieve original lower bound of variable. + + Returns + ------- + float + + """ return SCIPvarGetLbOriginal(self.scip_var) def getUbOriginal(self): - """Retrieve original upper bound of variable""" + """ + Retrieve original upper bound of variable. + + Returns + ------- + float + + """ return SCIPvarGetUbOriginal(self.scip_var) def getLbGlobal(self): - """Retrieve global lower bound of variable""" + """ + Retrieve global lower bound of variable. + + Returns + ------- + float + + """ return SCIPvarGetLbGlobal(self.scip_var) def getUbGlobal(self): - """Retrieve global upper bound of variable""" + """ + Retrieve global upper bound of variable. + + Returns + ------- + float + + """ return SCIPvarGetUbGlobal(self.scip_var) def getLbLocal(self): - """Retrieve current lower bound of variable""" + """ + Retrieve current lower bound of variable. + + Returns + ------- + float + + """ return SCIPvarGetLbLocal(self.scip_var) def getUbLocal(self): - """Retrieve current upper bound of variable""" + """ + Retrieve current upper bound of variable. + + Returns + ------- + float + + """ return SCIPvarGetUbLocal(self.scip_var) def getObj(self): - """Retrieve current objective value of variable""" + """ + Retrieve current objective value of variable. + + Returns + ------- + float + + """ return SCIPvarGetObj(self.scip_var) def getLPSol(self): - """Retrieve the current LP solution value of variable""" + """ + Retrieve the current LP solution value of variable. + + Returns + ------- + float + + """ return SCIPvarGetLPSol(self.scip_var) def getAvgSol(self): - """Get the weighted average solution of variable in all feasible primal solutions found""" + """ + Get the weighted average solution of variable in all feasible primal solutions found. + + Returns + ------- + float + + """ return SCIPvarGetAvgSol(self.scip_var) def varMayRound(self, direction="down"): - """Checks whether its it possible, to round variable up and stay feasible for the relaxation + """ + Checks whether it is possible to round variable up / down and stay feasible for the relaxation. - :param direction: direction in which the variable will be rounded + Parameters + ---------- + direction : str + "up" or "down" + + Returns + ------- + bool """ if direction not in ("down", "up"): @@ -925,6 +1602,20 @@ cdef class Constraint: @staticmethod cdef create(SCIP_CONS* scipcons): + """ + Main method for creating a Constraint class. Is used instead of __init__. + + Parameters + ---------- + scipcons : SCIP_CONS* + A pointer to the SCIP_CONS + + Returns + ------- + cons : Constraint + The Python representative of the SCIP_CONS + + """ if scipcons == NULL: raise Warning("cannot create Constraint with SCIP_CONS* == NULL") cons = Constraint() @@ -940,65 +1631,170 @@ cdef class Constraint: return self.name def isOriginal(self): - """Retrieve whether the constraint belongs to the original problem""" + """ + Retrieve whether the constraint belongs to the original problem. + + Returns + ------- + bool + + """ return SCIPconsIsOriginal(self.scip_cons) def isInitial(self): - """Retrieve True if the relaxation of the constraint should be in the initial LP""" + """ + Returns True if the relaxation of the constraint should be in the initial LP. + + Returns + ------- + bool + + """ return SCIPconsIsInitial(self.scip_cons) def isSeparated(self): - """Retrieve True if constraint should be separated during LP processing""" + """ + Returns True if constraint should be separated during LP processing. + + Returns + ------- + bool + + """ return SCIPconsIsSeparated(self.scip_cons) def isEnforced(self): - """Retrieve True if constraint should be enforced during node processing""" + """ + Returns True if constraint should be enforced during node processing. + + Returns + ------- + bool + + """ return SCIPconsIsEnforced(self.scip_cons) def isChecked(self): - """Retrieve True if constraint should be checked for feasibility""" + """ + Returns True if constraint should be checked for feasibility. + + Returns + ------- + bool + + """ return SCIPconsIsChecked(self.scip_cons) def isPropagated(self): - """Retrieve True if constraint should be propagated during node processing""" + """ + Returns True if constraint should be propagated during node processing. + + Returns + ------- + bool + + """ return SCIPconsIsPropagated(self.scip_cons) def isLocal(self): - """Retrieve True if constraint is only locally valid or not added to any (sub)problem""" + """ + Returns True if constraint is only locally valid or not added to any (sub)problem. + + Returns + ------- + bool + + """ return SCIPconsIsLocal(self.scip_cons) def isModifiable(self): - """Retrieve True if constraint is modifiable (subject to column generation)""" + """ + Returns True if constraint is modifiable (subject to column generation). + + Returns + ------- + bool + + """ return SCIPconsIsModifiable(self.scip_cons) def isDynamic(self): - """Retrieve True if constraint is subject to aging""" + """ + Returns True if constraint is subject to aging. + + Returns + ------- + bool + + """ return SCIPconsIsDynamic(self.scip_cons) def isRemovable(self): - """Retrieve True if constraint's relaxation should be removed from the LP due to aging or cleanup""" + """ + Returns True if constraint's relaxation should be removed from the LP due to aging or cleanup. + + Returns + ------- + bool + + """ return SCIPconsIsRemovable(self.scip_cons) def isStickingAtNode(self): - """Retrieve True if constraint is only locally valid or not added to any (sub)problem""" + """ + Returns True if constraint is only locally valid or not added to any (sub)problem. + + Returns + ------- + bool + + """ return SCIPconsIsStickingAtNode(self.scip_cons) def isActive(self): - """returns True iff constraint is active in the current node""" + """ + Returns True iff constraint is active in the current node. + + Returns + ------- + bool + + """ return SCIPconsIsActive(self.scip_cons) def isLinear(self): - """Retrieve True if constraint is linear""" + """ + Returns True if constraint is linear + + Returns + ------- + bool + + """ constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8') return constype == 'linear' def isNonlinear(self): - """Retrieve True if constraint is nonlinear""" + """ + Returns True if constraint is nonlinear. + + Returns + ------- + bool + + """ constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8') return constype == 'nonlinear' def getConshdlrName(self): - """Return the constraint handler's name""" + """ + Return the constraint handler's name. + + Returns + ------- + str + + """ constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8') return constype @@ -1023,18 +1819,30 @@ cdef void relayErrorMessage(void *messagehdlr, FILE *file, const char *msg) noex #@anchor Model ## cdef class Model: - """Main class holding a pointer to SCIP for managing most interactions""" def __init__(self, problemName='model', defaultPlugins=True, Model sourceModel=None, origcopy=False, globalcopy=True, enablepricing=False, createscip=True, threadsafe=False): """ - :param problemName: name of the problem (default 'model') - :param defaultPlugins: use default plugins? (default True) - :param sourceModel: create a copy of the given Model instance (default None) - :param origcopy: whether to call copy or copyOrig (default False) - :param globalcopy: whether to create a global or a local copy (default True) - :param enablepricing: whether to enable pricing in copy (default False) - :param createscip: initialize the Model object and creates a SCIP instance - :param threadsafe: False if data can be safely shared between the source and target problem + Main class holding a pointer to SCIP for managing most interactions + + Parameters + ---------- + problemName : str, optional + name of the problem (default 'model') + defaultPlugins : bool, optional + use default plugins? (default True) + sourceModel : Model or None, optional + create a copy of the given Model instance (default None) + origcopy : bool, optional + whether to call copy or copyOrig (default False) + globalcopy : bool, optional + whether to create a global or a local copy (default True) + enablepricing : bool, optional + whether to enable pricing in copy (default False) + createscip : bool, optional + initialize the Model object and creates a SCIP instance (default True) + threadsafe : bool, optional + False if data can be safely shared between the source and target problem (default False) + """ if self.version() < MAJOR: raise Exception("linked SCIP is not compatible to this version of PySCIPOpt - use at least version", MAJOR) @@ -1068,11 +1876,12 @@ cdef class Model: def attachEventHandlerCallback(self, callback, - events: List[SCIP_EVENTTYPE], + events, name="eventhandler", description="" ): - """Attach an event handler to the model using a callback function. + """ + Attach an event handler to the model using a callback function. Parameters ---------- @@ -1130,7 +1939,18 @@ cdef class Model: @staticmethod cdef create(SCIP* scip): - """Creates a model and appropriately assigns the scip and bestsol parameters + """ + Creates a model and appropriately assigns the scip and bestsol parameters. + + Parameters + ---------- + scip : SCIP* + A pointer to a SCIP object + + Returns + ------- + Model + """ if scip == NULL: raise Warning("cannot create Model with SCIP* == NULL") @@ -1141,26 +1961,48 @@ cdef class Model: @property def _freescip(self): - """Return whether the underlying Scip pointer gets deallocted when the current + """ + Return whether the underlying Scip pointer gets deallocted when the current object is deleted. + + Returns + ------- + bool + """ return self._freescip @_freescip.setter def _freescip(self, val): - """Set whether the underlying Scip pointer gets deallocted when the current + """ + Set whether the underlying Scip pointer gets deallocted when the current object is deleted. + + Parameters + ---------- + val : bool + """ self._freescip = val @cython.always_allow_keywords(True) @staticmethod def from_ptr(capsule, take_ownership): - """Create a Model from a given pointer. + """ + Create a Model from a given pointer. + + Parameters + ---------- + capsule + The PyCapsule containing the SCIP pointer under the name "scip" + take_ownership : bool + Whether the newly created Model assumes ownership of the + underlying Scip pointer (see ``_freescip``) + + Returns + ------- + Model - :param cpasule: The PyCapsule containing the SCIP pointer under the name "scip". - :param take_ownership: Whether the newly created Model assumes ownership of the - underlying Scip pointer (see `_freescip`). """ if not PyCapsule_IsValid(capsule, "scip"): raise ValueError("The given capsule does not contain a valid scip pointer") @@ -1170,12 +2012,21 @@ cdef class Model: @cython.always_allow_keywords(True) def to_ptr(self, give_ownership): - """Return the underlying Scip pointer to the current Model. + """ + Return the underlying Scip pointer to the current Model. + + Parameters + ---------- + give_ownership : bool + Whether the current Model gives away ownership of the + underlying Scip pointer (see ``_freescip``) + + Returns + ------- + capsule + The underlying pointer to the current Model, wrapped in a + PyCapsule under the name "scip". - :param give_ownership: Whether the current Model gives away ownership of the - underlying Scip pointer (see `_freescip`). - :return capsule: The underlying pointer to the current Model, wrapped in a - PyCapsule under the name "scip". """ capsule = PyCapsule_New(self._scip, "scip", NULL) if give_ownership: @@ -1183,24 +2034,29 @@ cdef class Model: return capsule def includeDefaultPlugins(self): - """Includes all default plug-ins into SCIP""" + """Includes all default plug-ins into SCIP.""" PY_SCIP_CALL(SCIPincludeDefaultPlugins(self._scip)) def createProbBasic(self, problemName='model'): - """Create new problem instance with given name + """ + Create new problem instance with given name. - :param problemName: name of model or problem (Default value = 'model') + Parameters + ---------- + problemName : str, optional + name of model or problem (Default value = 'model') """ n = str_conversion(problemName) PY_SCIP_CALL(SCIPcreateProbBasic(self._scip, n)) def freeProb(self): - """Frees problem and solution process data""" + """Frees problem and solution process data.""" PY_SCIP_CALL(SCIPfreeProb(self._scip)) def freeTransform(self): - """Frees all solution process data including presolving and transformed problem, only original problem is kept""" + """Frees all solution process data including presolving and + transformed problem, only original problem is kept.""" self._modelvars = { var: value for var, value in self._modelvars.items() @@ -1209,11 +2065,18 @@ cdef class Model: PY_SCIP_CALL(SCIPfreeTransform(self._scip)) def version(self): - """Retrieve SCIP version""" + """ + Retrieve SCIP version. + + Returns + ------- + float + + """ return SCIPversion() def printVersion(self): - """Print version, copyright information and compile mode""" + """Print version, copyright information and compile mode.""" user_locale = locale.getlocale(category=locale.LC_NUMERIC) locale.setlocale(locale.LC_NUMERIC, "C") @@ -1222,7 +2085,7 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def printExternalCodeVersions(self): - """Print external code versions, e.g. symmetry, non-linear solver, lp solver""" + """Print external code versions, e.g. symmetry, non-linear solver, lp solver.""" user_locale = locale.getlocale(category=locale.LC_NUMERIC) locale.setlocale(locale.LC_NUMERIC, "C") @@ -1231,149 +2094,474 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def getProbName(self): - """Retrieve problem name""" + """ + Retrieve problem name. + + Returns + ------- + str + + """ return bytes(SCIPgetProbName(self._scip)).decode('UTF-8') def getTotalTime(self): - """Retrieve the current total SCIP time in seconds, i.e. the total time since the SCIP instance has been created""" + """ + Retrieve the current total SCIP time in seconds, + i.e. the total time since the SCIP instance has been created. + + Returns + ------- + float + + """ return SCIPgetTotalTime(self._scip) def getSolvingTime(self): - """Retrieve the current solving time in seconds""" + """ + Retrieve the current solving time in seconds. + + Returns + ------- + float + + """ return SCIPgetSolvingTime(self._scip) def getReadingTime(self): - """Retrieve the current reading time in seconds""" + """ + Retrieve the current reading time in seconds. + + Returns + ------- + float + + """ return SCIPgetReadingTime(self._scip) def getPresolvingTime(self): - """Retrieve the curernt presolving time in seconds""" + """ + Returns the current presolving time in seconds. + + Returns + ------- + float + + """ return SCIPgetPresolvingTime(self._scip) def getNLPIterations(self): - """Retrieve the total number of LP iterations so far.""" + """ + Returns the total number of LP iterations so far. + + Returns + ------- + int + + """ return SCIPgetNLPIterations(self._scip) def getNNodes(self): - """gets number of processed nodes in current run, including the focus node.""" + """ + Gets number of processed nodes in current run, including the focus node. + + Returns + ------- + int + + """ return SCIPgetNNodes(self._scip) def getNTotalNodes(self): - """gets number of processed nodes in all runs, including the focus node.""" + """ + Gets number of processed nodes in all runs, including the focus node. + + Returns + ------- + int + + """ return SCIPgetNTotalNodes(self._scip) def getNFeasibleLeaves(self): - """Retrieve number of leaf nodes processed with feasible relaxation solution.""" + """ + Retrieve number of leaf nodes processed with feasible relaxation solution. + + Returns + ------- + int + + """ return SCIPgetNFeasibleLeaves(self._scip) def getNInfeasibleLeaves(self): - """gets number of infeasible leaf nodes processed.""" + """ + Gets number of infeasible leaf nodes processed. + + Returns + ------- + int + + """ return SCIPgetNInfeasibleLeaves(self._scip) def getNLeaves(self): - """gets number of leaves in the tree.""" + """ + Gets number of leaves in the tree. + + Returns + ------- + int + + """ return SCIPgetNLeaves(self._scip) def getNChildren(self): - """gets number of children of focus node.""" + """ + Gets number of children of focus node. + + Returns + ------- + int + + """ return SCIPgetNChildren(self._scip) def getNSiblings(self): - """gets number of siblings of focus node.""" + """ + Gets number of siblings of focus node. + + Returns + ------- + int + + """ return SCIPgetNSiblings(self._scip) def getCurrentNode(self): - """Retrieve current node.""" + """ + Retrieve current node. + + Returns + ------- + Node + + """ return Node.create(SCIPgetCurrentNode(self._scip)) def getGap(self): - """Retrieve the gap, i.e. |(primalbound - dualbound)/min(|primalbound|,|dualbound|)|.""" + """ + Retrieve the gap, + i.e. abs((primalbound - dualbound)/min(abs(primalbound),abs(dualbound))) + + Returns + ------- + float + + """ return SCIPgetGap(self._scip) def getDepth(self): - """Retrieve the depth of the current node""" + """ + Retrieve the depth of the current node. + + Returns + ------- + int + + """ return SCIPgetDepth(self._scip) def infinity(self): - """Retrieve SCIP's infinity value""" + """ + Retrieve SCIP's infinity value. + + Returns + ------- + int + + """ return SCIPinfinity(self._scip) def epsilon(self): - """Retrieve epsilon for e.g. equality checks""" + """ + Retrieve epsilon for e.g. equality checks. + + Returns + ------- + float + + """ return SCIPepsilon(self._scip) def feastol(self): - """Retrieve feasibility tolerance""" + """ + Retrieve feasibility tolerance. + + Returns + ------- + float + + """ return SCIPfeastol(self._scip) def feasFrac(self, value): - """returns fractional part of value, i.e. x - floor(x) in feasible tolerance: x - floor(x+feastol)""" + """ + Returns fractional part of value, i.e. x - floor(x) in feasible tolerance: x - floor(x+feastol). + + Parameters + ---------- + value : float + + Returns + ------- + float + + """ return SCIPfeasFrac(self._scip, value) def frac(self, value): - """returns fractional part of value, i.e. x - floor(x) in epsilon tolerance: x - floor(x+eps)""" + """ + Returns fractional part of value, i.e. x - floor(x) in epsilon tolerance: x - floor(x+eps). + + Parameters + ---------- + value : float + + Returns + ------- + float + + """ return SCIPfrac(self._scip, value) def feasFloor(self, value): - """ rounds value + feasibility tolerance down to the next integer""" + """ + Rounds value + feasibility tolerance down to the next integer. + + Parameters + ---------- + value : float + + Returns + ------- + float + + """ return SCIPfeasFloor(self._scip, value) def feasCeil(self, value): - """rounds value - feasibility tolerance up to the next integer""" + """ + Rounds value - feasibility tolerance up to the next integer. + + Parameters + ---------- + value : float + + Returns + ------- + float + + """ return SCIPfeasCeil(self._scip, value) def feasRound(self, value): - """rounds value to the nearest integer in feasibility tolerance""" + """ + Rounds value to the nearest integer in feasibility tolerance. + + Parameters + ---------- + value : float + + Returns + ------- + float + + """ return SCIPfeasRound(self._scip, value) def isZero(self, value): - """returns whether abs(value) < eps""" + """ + Returns whether abs(value) < eps. + + Parameters + ---------- + value : float + + Returns + ------- + bool + + """ return SCIPisZero(self._scip, value) def isFeasZero(self, value): - """returns whether abs(value) < feastol""" + """ + Returns whether abs(value) < feastol. + + Parameters + ---------- + value : float + + Returns + ------- + bool + + """ return SCIPisFeasZero(self._scip, value) def isInfinity(self, value): - """returns whether value is SCIP's infinity""" + """ + Returns whether value is SCIP's infinity. + + Parameters + ---------- + value : float + + Returns + ------- + bool + + """ return SCIPisInfinity(self._scip, value) def isFeasNegative(self, value): - """returns whether value < -feastol""" + """ + Returns whether value < -feastol. + + Parameters + ---------- + value : float + + Returns + ------- + bool + + """ return SCIPisFeasNegative(self._scip, value) def isFeasIntegral(self, value): - """returns whether value is integral within the LP feasibility bounds""" + """ + Returns whether value is integral within the LP feasibility bounds. + + Parameters + ---------- + value : float + + Returns + ------- + bool + + """ return SCIPisFeasIntegral(self._scip, value) def isEQ(self, val1, val2): - """checks, if values are in range of epsilon""" + """ + Checks, if values are in range of epsilon. + + Parameters + ---------- + val1 : float + val2 : float + + Returns + ------- + bool + + """ return SCIPisEQ(self._scip, val1, val2) def isFeasEQ(self, val1, val2): - """checks, if relative difference of values is in range of feasibility tolerance""" + """ + Checks, if relative difference of values is in range of feasibility tolerance. + + Parameters + ---------- + val1 : float + val2 : float + + Returns + ------- + bool + + """ return SCIPisFeasEQ(self._scip, val1, val2) def isLE(self, val1, val2): - """returns whether val1 <= val2 + eps""" + """ + Returns whether val1 <= val2 + eps. + + Parameters + ---------- + val1 : float + val2 : float + + Returns + ------- + bool + + """ return SCIPisLE(self._scip, val1, val2) def isLT(self, val1, val2): - """returns whether val1 < val2 - eps""" + """ + Returns whether val1 < val2 - eps. + + Parameters + ---------- + val1 : float + val2 : float + + Returns + ------- + bool + + """ return SCIPisLT(self._scip, val1, val2) def isGE(self, val1, val2): - """returns whether val1 >= val2 - eps""" + """ + Returns whether val1 >= val2 - eps. + + Parameters + ---------- + val1 : float + val2 : float + + Returns + ------- + bool + + """ return SCIPisGE(self._scip, val1, val2) def isGT(self, val1, val2): - """returns whether val1 > val2 + eps""" + """ + Returns whether val1 > val2 + eps. + + Parameters + ---------- + val1 : float + val2 : foat + + Returns + ------- + bool + + """ return SCIPisGT(self._scip, val1, val2) - def getCondition(self, exact=False): - """Get the current LP's condition number + def getCondition(self, exact=False): + """ + Get the current LP's condition number. + + Parameters + ---------- + exact : bool, optional + whether to get an estimate or the exact value (Default value = False) - :param exact: whether to get an estimate or the exact value (Default value = False) + Returns + ------- + float """ cdef SCIP_LPI* lpi @@ -1387,11 +2575,26 @@ cdef class Model: return quality def enableReoptimization(self, enable=True): - """include specific heuristics and branching rules for reoptimization""" + """ + Include specific heuristics and branching rules for reoptimization. + + Parameters + ---------- + enable : bool, optional + True to enable and False to disable + + """ PY_SCIP_CALL(SCIPenableReoptimization(self._scip, enable)) def lpiGetIterations(self): - """Get the iteration count of the last solved LP""" + """ + Get the iteration count of the last solved LP. + + Returns + ------- + int + + """ cdef SCIP_LPI* lpi PY_SCIP_CALL(SCIPgetLPI(self._scip, &lpi)) cdef int iters = 0 @@ -1409,24 +2612,41 @@ cdef class Model: PY_SCIP_CALL(SCIPsetObjsense(self._scip, SCIP_OBJSENSE_MAXIMIZE)) def setObjlimit(self, objlimit): - """Set a limit on the objective function. + """ + Set a limit on the objective function. Only solutions with objective value better than this limit are accepted. - :param objlimit: limit on the objective function + Parameters + ---------- + objlimit : float + limit on the objective function """ PY_SCIP_CALL(SCIPsetObjlimit(self._scip, objlimit)) def getObjlimit(self): - """returns current limit on objective function.""" + """ + Returns current limit on objective function. + + Returns + ------- + float + + """ return SCIPgetObjlimit(self._scip) def setObjective(self, expr, sense = 'minimize', clear = 'true'): - """Establish the objective function as a linear expression. + """ + Establish the objective function as a linear expression. - :param expr: the objective function SCIP Expr, or constant value - :param sense: the objective sense (Default value = 'minimize') - :param clear: set all other variables objective coefficient to zero (Default value = 'true') + Parameters + ---------- + expr : Expr or float + the objective function SCIP Expr, or constant value + sense : str, optional + the objective sense ("minimize" or "maximize") (Default value = 'minimize') + clear : bool, optional + set all other variables objective coefficient to zero (Default value = 'true') """ @@ -1467,7 +2687,14 @@ cdef class Model: raise Warning("unrecognized optimization sense: %s" % sense) def getObjective(self): - """Retrieve objective function as Expr""" + """ + Retrieve objective function as Expr. + + Returns + ------- + Expr + + """ variables = self.getVars() objective = Expr() for var in variables: @@ -1478,10 +2705,15 @@ cdef class Model: return objective def addObjoffset(self, offset, solutions = False): - """Add constant offset to objective + """ + Add constant offset to objective. - :param offset: offset to add - :param solutions: add offset also to existing solutions (Default value = False) + Parameters + ---------- + offset : float + offset to add + solutions : bool, optional + add offset also to existing solutions (Default value = False) """ if solutions: @@ -1490,9 +2722,17 @@ cdef class Model: PY_SCIP_CALL(SCIPaddOrigObjoffset(self._scip, offset)) def getObjoffset(self, original = True): - """Retrieve constant objective offset + """ + Retrieve constant objective offset + + Parameters + ---------- + original : bool, optional + offset of original or transformed problem (Default value = True) - :param original: offset of original or transformed problem (Default value = True) + Returns + ------- + float """ if original: @@ -1501,19 +2741,31 @@ cdef class Model: return SCIPgetTransObjoffset(self._scip) def setObjIntegral(self): - """informs SCIP that the objective value is always integral in every feasible solution - Note: This function should be used to inform SCIP that the objective function is integral, helping to improve the - performance. This is useful when using column generation. If no column generation (pricing) is used, SCIP - automatically detects whether the objective function is integral or can be scaled to be integral. However, in - any case, the user has to make sure that no variable is added during the solving process that destroys this - property. + """Informs SCIP that the objective value is always integral in every feasible solution. + + Notes + ----- + This function should be used to inform SCIP that the objective function is integral, + helping to improve the performance. This is useful when using column generation. + If no column generation (pricing) is used, SCIP automatically detects whether the objective + function is integral or can be scaled to be integral. However, in any case, the user has to + make sure that no variable is added during the solving process that destroys this property. """ PY_SCIP_CALL(SCIPsetObjIntegral(self._scip)) def getLocalEstimate(self, original = False): - """gets estimate of best primal solution w.r.t. original or transformed problem contained in current subtree + """ + Gets estimate of best primal solution w.r.t. original or transformed problem contained in current subtree. + + Parameters + ---------- + original : bool, optional + get estimate of original or transformed problem (Default value = False) + + Returns + ------- + float - :param original: estimate of original or transformed problem (Default value = False) """ if original: return SCIPgetLocalOrigEstimate(self._scip) @@ -1522,38 +2774,62 @@ cdef class Model: # Setting parameters def setPresolve(self, setting): - """Set presolving parameter settings. + """ + Set presolving parameter settings. + - :param setting: the parameter settings (SCIP_PARAMSETTING) + Parameters + ---------- + setting : SCIP_PARAMSETTING + the parameter settings, e.g. SCIP_PARAMSETTING.OFF """ PY_SCIP_CALL(SCIPsetPresolving(self._scip, setting, True)) def setProbName(self, name): - """Set problem name""" + """ + Set problem name. + + Parameters + ---------- + name : str + + """ n = str_conversion(name) PY_SCIP_CALL(SCIPsetProbName(self._scip, n)) def setSeparating(self, setting): - """Set separating parameter settings. + """ + Set separating parameter settings. - :param setting: the parameter settings (SCIP_PARAMSETTING) + Parameters + ---------- + setting : SCIP_PARAMSETTING + the parameter settings, e.g. SCIP_PARAMSETTING.OFF """ PY_SCIP_CALL(SCIPsetSeparating(self._scip, setting, True)) def setHeuristics(self, setting): - """Set heuristics parameter settings. + """ + Set heuristics parameter settings. - :param setting: the parameter setting (SCIP_PARAMSETTING) + Parameters + ---------- + setting : SCIP_PARAMSETTING + the parameter settings, e.g. SCIP_PARAMSETTING.OFF """ PY_SCIP_CALL(SCIPsetHeuristics(self._scip, setting, True)) def disablePropagation(self, onlyroot=False): - """Disables propagation in SCIP to avoid modifying the original problem during transformation. + """ + Disables propagation in SCIP to avoid modifying the original problem during transformation. - :param onlyroot: use propagation when root processing is finished (Default value = False) + Parameters + ---------- + onlyroot : bool, optional + use propagation when root processing is finished (Default value = False) """ self.setIntParam("propagating/maxroundsroot", 0) @@ -1561,12 +2837,23 @@ cdef class Model: self.setIntParam("propagating/maxrounds", 0) def writeProblem(self, filename='model.cip', trans=False, genericnames=False, verbose=True): - """Write current model/problem to a file. + """ + Write current model/problem to a file. + + Parameters + ---------- + filename : str, optional + the name of the file to be used (Default value = 'model.cip'). + Should have an extension corresponding to one of the readable file formats, + described in https://www.scipopt.org/doc/html/group__FILEREADERS.php. + trans : bool, optional + indicates whether the transformed problem is written to file (Default value = False) + genericnames : bool, optional + indicates whether the problem should be written with generic variable + and constraint names (Default value = False) + verbose : bool, optional + indicates whether a success message should be printed - :param filename: the name of the file to be used (Default value = 'model.cip'). Should have an extension corresponding to one of the readable file formats, described in https://www.scipopt.org/doc/html/group__FILEREADERS.php. - :param trans: indicates whether the transformed problem is written to file (Default value = False) - :param genericnames: indicates whether the problem should be written with generic variable and constraint names (Default value = False) - :param verbose: indicates whether a success message should be printed """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) locale.setlocale(locale.LC_NUMERIC, "C") @@ -1593,16 +2880,30 @@ cdef class Model: # Variable Functions def addVar(self, name='', vtype='C', lb=0.0, ub=None, obj=0.0, pricedVar=False, pricedVarScore=1.0): - """Create a new variable. Default variable is non-negative and continuous. + """ + Create a new variable. Default variable is non-negative and continuous. - :param name: name of the variable, generic if empty (Default value = '') - :param vtype: type of the variable: 'C' continuous, 'I' integer, 'B' binary, and 'M' implicit integer - (see https://www.scipopt.org/doc/html/FAQ.php#implicitinteger) (Default value = 'C') - :param lb: lower bound of the variable, use None for -infinity (Default value = 0.0) - :param ub: upper bound of the variable, use None for +infinity (Default value = None) - :param obj: objective value of variable (Default value = 0.0) - :param pricedVar: is the variable a pricing candidate? (Default value = False) - :param pricedVarScore: score of variable in case it is priced, the higher the better (Default value = 1.0) + Parameters + ---------- + name : str, optional + name of the variable, generic if empty (Default value = '') + vtype : str, optional + type of the variable: 'C' continuous, 'I' integer, 'B' binary, and 'M' implicit integer + (Default value = 'C') + lb : float or None, optional + lower bound of the variable, use None for -infinity (Default value = 0.0) + ub : float or None, optional + upper bound of the variable, use None for +infinity (Default value = None) + obj : float, optional + objective value of variable (Default value = 0.0) + pricedVar : bool, optional + is the variable a pricing candidate? (Default value = False) + pricedVarScore : float, optional + score of variable in case it is priced, the higher the better (Default value = 1.0) + + Returns + ------- + Variable """ cdef SCIP_VAR* scip_var @@ -1651,9 +2952,17 @@ cdef class Model: return pyVar def getTransformedVar(self, Variable var): - """Retrieve the transformed variable. + """ + Retrieve the transformed variable. - :param Variable var: original variable to get the transformed of + Parameters + ---------- + var : Variable + original variable to get the transformed of + + Returns + ------- + Variable """ cdef SCIP_VAR* _tvar @@ -1662,21 +2971,38 @@ cdef class Model: return Variable.create(_tvar) def addVarLocks(self, Variable var, nlocksdown, nlocksup): - """adds given values to lock numbers of variable for rounding + """ + Adds given values to lock numbers of variable for rounding. - :param Variable var: variable to adjust the locks for - :param nlocksdown: new number of down locks - :param nlocksup: new number of up locks + Parameters + ---------- + var : Variable + variable to adjust the locks for + nlocksdown : int + new number of down locks + nlocksup : int + new number of up locks """ PY_SCIP_CALL(SCIPaddVarLocks(self._scip, var.scip_var, nlocksdown, nlocksup)) def fixVar(self, Variable var, val): - """Fixes the variable var to the value val if possible. + """ + Fixes the variable var to the value val if possible. + + Parameters + ---------- + var : Variable + variable to fix + val : float + the fix value - :param Variable var: variable to fix - :param val: float, the fix value - :return: tuple (infeasible, fixed) of booleans + Returns + ------- + infeasible : bool + Is the fixing infeasible? + fixed : bool + Was the fixing performed? """ cdef SCIP_Bool infeasible @@ -1685,10 +3011,18 @@ cdef class Model: return infeasible, fixed def delVar(self, Variable var): - """Delete a variable. + """ + Delete a variable. + + Parameters + ---------- + var : Variable + the variable which shall be deleted - :param var: the variable which shall be deleted - :return: bool, was deleting succesful + Returns + ------- + deleted : bool + Whether deleting was successfull """ cdef SCIP_Bool deleted @@ -1698,14 +3032,24 @@ cdef class Model: return deleted def tightenVarLb(self, Variable var, lb, force=False): - """Tighten the lower bound in preprocessing or current node, if the bound is tighter. + """ + Tighten the lower bound in preprocessing or current node, if the bound is tighter. + + Parameters + ---------- + var : Variable + SCIP variable + lb : float + possible new lower bound + force : bool, optional + force tightening even if below bound strengthening tolerance (default = False) - :param var: SCIP variable - :param lb: possible new lower bound - :param force: force tightening even if below bound strengthening tolerance - :return: tuple of bools, (infeasible, tightened) - infeasible: whether new domain is empty - tightened: whether the bound was tightened + Returns + ------- + infeasible : bool + Whether new domain is empty + tightened : bool + Whether the bound was tightened """ cdef SCIP_Bool infeasible @@ -1714,14 +3058,24 @@ cdef class Model: return infeasible, tightened def tightenVarUb(self, Variable var, ub, force=False): - """Tighten the upper bound in preprocessing or current node, if the bound is tighter. + """ + Tighten the upper bound in preprocessing or current node, if the bound is tighter. + + Parameters + ---------- + var : Variable + SCIP variable + ub : float + possible new upper bound + force : bool, optional + force tightening even if below bound strengthening tolerance - :param var: SCIP variable - :param ub: possible new upper bound - :param force: force tightening even if below bound strengthening tolerance - :return: tuple of bools, (infeasible, tightened) - infeasible: whether new domain is empty - tightened: whether the bound was tightened + Returns + ------- + infeasible : bool + Whether new domain is empty + tightened : bool + Whether the bound was tightened """ cdef SCIP_Bool infeasible @@ -1730,14 +3084,24 @@ cdef class Model: return infeasible, tightened def tightenVarUbGlobal(self, Variable var, ub, force=False): - """Tighten the global upper bound, if the bound is tighter. + """ + Tighten the global upper bound, if the bound is tighter. + + Parameters + ---------- + var : Variable + SCIP variable + ub : float + possible new upper bound + force : bool, optional + force tightening even if below bound strengthening tolerance - :param var: SCIP variable - :param ub: possible new upper bound - :param force: force tightening even if below bound strengthening tolerance - :return: tuple of bools, (infeasible, tightened) - infeasible: whether new domain is empty - tightened: whether the bound was tightened + Returns + ------- + infeasible : bool + Whether new domain is empty + tightened : bool + Whether the bound was tightened """ cdef SCIP_Bool infeasible @@ -1746,14 +3110,23 @@ cdef class Model: return infeasible, tightened def tightenVarLbGlobal(self, Variable var, lb, force=False): - """Tighten the global upper bound, if the bound is tighter. + """Tighten the global lower bound, if the bound is tighter. + + Parameters + ---------- + var : Variable + SCIP variable + lb : float + possible new lower bound + force : bool, optional + force tightening even if below bound strengthening tolerance - :param var: SCIP variable - :param lb: possible new upper bound - :param force: force tightening even if below bound strengthening tolerance - :return: tuple of bools, (infeasible, tightened) - infeasible: whether new domain is empty - tightened: whether the bound was tightened + Returns + ------- + infeasible : bool + Whether new domain is empty + tightened : bool + Whether the bound was tightened """ cdef SCIP_Bool infeasible @@ -1762,10 +3135,15 @@ cdef class Model: return infeasible, tightened def chgVarLb(self, Variable var, lb): - """Changes the lower bound of the specified variable. + """ + Changes the lower bound of the specified variable. - :param Variable var: variable to change bound of - :param lb: new lower bound (set to None for -infinity) + Parameters + ---------- + var : Variable + variable to change bound of + lb : float or None + new lower bound (set to None for -infinity) """ if lb is None: @@ -1775,8 +3153,12 @@ cdef class Model: def chgVarUb(self, Variable var, ub): """Changes the upper bound of the specified variable. - :param Variable var: variable to change bound of - :param ub: new upper bound (set to None for +infinity) + Parameters + ---------- + var : Variable + variable to change bound of + lb : float or None + new upper bound (set to None for +infinity) """ if ub is None: @@ -1786,8 +3168,12 @@ cdef class Model: def chgVarLbGlobal(self, Variable var, lb): """Changes the global lower bound of the specified variable. - :param Variable var: variable to change bound of - :param lb: new lower bound (set to None for -infinity) + Parameters + ---------- + var : Variable + variable to change bound of + lb : float or None + new lower bound (set to None for -infinity) """ if lb is None: @@ -1797,8 +3183,12 @@ cdef class Model: def chgVarUbGlobal(self, Variable var, ub): """Changes the global upper bound of the specified variable. - :param Variable var: variable to change bound of - :param ub: new upper bound (set to None for +infinity) + Parameters + ---------- + var : Variable + variable to change bound of + lb : float or None + new upper bound (set to None for +infinity) """ if ub is None: @@ -1808,8 +3198,15 @@ cdef class Model: def chgVarLbNode(self, Node node, Variable var, lb): """Changes the lower bound of the specified variable at the given node. - :param Variable var: variable to change bound of - :param lb: new lower bound (set to None for -infinity) + Parameters + ---------- + node : Node + Node at which the variable bound will be changed + var : Variable + variable to change bound of + lb : float or None + new lower bound (set to None for -infinity) + """ if lb is None: @@ -1819,8 +3216,14 @@ cdef class Model: def chgVarUbNode(self, Node node, Variable var, ub): """Changes the upper bound of the specified variable at the given node. - :param Variable var: variable to change bound of - :param ub: new upper bound (set to None for +infinity) + Parameters + ---------- + node : Node + Node at which the variable bound will be changed + var : Variable + variable to change bound of + lb : float or None + new upper bound (set to None for +infinity) """ if ub is None: @@ -1829,10 +3232,16 @@ cdef class Model: def chgVarType(self, Variable var, vtype): - """Changes the type of a variable + """ + Changes the type of a variable. - :param Variable var: variable to change type of - :param vtype: new variable type + Parameters + ---------- + var : Variable + variable to change type of + vtype : str + new variable type. 'C' or "CONTINUOUS", 'I' or "INTEGER", + 'B' or "BINARY", and 'M' "IMPLINT". """ cdef SCIP_Bool infeasible @@ -1850,9 +3259,17 @@ cdef class Model: print('could not change variable type of variable %s' % var) def getVars(self, transformed=False): - """Retrieve all variables. + """ + Retrieve all variables. + + Parameters + ---------- + transformed : bool, optional + Get transformed variables instead of original (Default value = False) - :param transformed: get transformed variables instead of original (Default value = False) + Returns + ------- + list of Variable """ cdef SCIP_VAR** _vars @@ -1883,9 +3300,18 @@ cdef class Model: return vars def getNVars(self, transformed=True): - """Retrieve number of variables in the problems. - - :param transformed: get transformed variables instead of original (Default value = True) + """ + Retrieve number of variables in the problems. + + Parameters + ---------- + transformed : bool, optional + Get transformed variables instead of original (Default value = True) + + Returns + ------- + int + """ if transformed: return SCIPgetNVars(self._scip) @@ -1893,40 +3319,80 @@ cdef class Model: return SCIPgetNOrigVars(self._scip) def getNIntVars(self): - """gets number of integer active problem variables""" + """ + Gets number of integer active problem variables. + + Returns + ------- + int + + """ return SCIPgetNIntVars(self._scip) def getNBinVars(self): - """gets number of binary active problem variables""" + """ + Gets number of binary active problem variables. + + Returns + ------- + int + + """ return SCIPgetNBinVars(self._scip) def getNImplVars(self): - """gets number of implicit integer active problem variables""" + """ + Gets number of implicit integer active problem variables. + + Returns + ------- + int + + """ return SCIPgetNImplVars(self._scip) def getNContVars(self): - """gets number of continuous active problem variables""" + """ + Gets number of continuous active problem variables. + + Returns + ------- + int + + """ return SCIPgetNContVars(self._scip) def getVarDict(self): - """gets dictionary with variables names as keys and current variable values as items""" + """ + Gets dictionary with variables names as keys and current variable values as items. + + Returns + ------- + dict of str to float + + """ var_dict = {} for var in self.getVars(): var_dict[var.name] = self.getVal(var) return var_dict def updateNodeLowerbound(self, Node node, lb): - """if given value is larger than the node's lower bound (in transformed problem), - sets the node's lower bound to the new value + """ + If given value is larger than the node's lower bound (in transformed problem), + sets the node's lower bound to the new value. - :param node: Node, the node to update - :param newbound: float, new bound (if greater) for the node + Parameters + ---------- + node : Node + the node to update + lb : float + new bound (if greater) for the node """ PY_SCIP_CALL(SCIPupdateNodeLowerbound(self._scip, node.scip_node, lb)) def relax(self): - """Relaxes the integrality restrictions of the model""" + """Relaxes the integrality restrictions of the model.""" if self.getStage() != SCIP_STAGE_PROBLEM: raise Warning("method can only be called in stage PROBLEM") @@ -1935,37 +3401,93 @@ cdef class Model: # Node methods def getBestChild(self): - """gets the best child of the focus node w.r.t. the node selection strategy.""" + """ + Gets the best child of the focus node w.r.t. the node selection strategy. + + Returns + ------- + Node + + """ return Node.create(SCIPgetBestChild(self._scip)) def getBestSibling(self): - """gets the best sibling of the focus node w.r.t. the node selection strategy.""" + """ + Gets the best sibling of the focus node w.r.t. the node selection strategy. + + Returns + ------- + Node + + """ return Node.create(SCIPgetBestSibling(self._scip)) def getPrioChild(self): - """gets the best child of the focus node w.r.t. the node selection priority assigned by the branching rule.""" + """ + Gets the best child of the focus node w.r.t. the node selection priority + assigned by the branching rule. + + Returns + ------- + Node + + """ return Node.create(SCIPgetPrioChild(self._scip)) def getPrioSibling(self): - """gets the best sibling of the focus node w.r.t. the node selection priority assigned by the branching rule.""" + """Gets the best sibling of the focus node w.r.t. + the node selection priority assigned by the branching rule. + + Returns + ------- + Node + + """ return Node.create(SCIPgetPrioSibling(self._scip)) def getBestLeaf(self): - """gets the best leaf from the node queue w.r.t. the node selection strategy.""" + """Gets the best leaf from the node queue w.r.t. the node selection strategy. + + Returns + ------- + Node + + """ return Node.create(SCIPgetBestLeaf(self._scip)) def getBestNode(self): - """gets the best node from the tree (child, sibling, or leaf) w.r.t. the node selection strategy.""" + """Gets the best node from the tree (child, sibling, or leaf) w.r.t. the node selection strategy. + + Returns + ------- + Node + + """ return Node.create(SCIPgetBestNode(self._scip)) def getBestboundNode(self): - """gets the node with smallest lower bound from the tree (child, sibling, or leaf).""" + """Gets the node with smallest lower bound from the tree (child, sibling, or leaf). + + Returns + ------- + Node + + """ return Node.create(SCIPgetBestboundNode(self._scip)) def getOpenNodes(self): - """access to all data of open nodes (leaves, children, and siblings) + """ + Access to all data of open nodes (leaves, children, and siblings). + + Returns + ------- + leaves : list of Node + list of all open leaf nodes + children : list of Node + list of all open children nodes + siblings : list of Node + list of all open sibling nodes - :return: three lists containing open leaves, children, siblings """ cdef SCIP_NODE** _leaves cdef SCIP_NODE** _children @@ -1983,21 +3505,33 @@ cdef class Model: return leaves, children, siblings def repropagateNode(self, Node node): - """marks the given node to be propagated again the next time a node of its subtree is processed""" + """Marks the given node to be propagated again the next time a node of its subtree is processed.""" PY_SCIP_CALL(SCIPrepropagateNode(self._scip, node.scip_node)) # LP Methods def getLPSolstat(self): - """Gets solution status of current LP""" + """ + Gets solution status of current LP. + + Returns + ------- + SCIP_LPSOLSTAT + + """ return SCIPgetLPSolstat(self._scip) def constructLP(self): - """makes sure that the LP of the current node is loaded and - may be accessed through the LP information methods + """ + Makes sure that the LP of the current node is loaded and + may be accessed through the LP information methods. + - :return: bool cutoff, i.e. can the node be cut off? + Returns + ------- + cutoff : bool + Can the node be cutoff? """ cdef SCIP_Bool cutoff @@ -2005,12 +3539,26 @@ cdef class Model: return cutoff def getLPObjVal(self): - """gets objective value of current LP (which is the sum of column and loose objective value)""" + """ + Gets objective value of current LP (which is the sum of column and loose objective value). + + Returns + ------- + float + + """ return SCIPgetLPObjval(self._scip) def getLPColsData(self): - """Retrieve current LP columns""" + """ + Retrieve current LP columns. + + Returns + ------- + list of Column + + """ cdef SCIP_COL** cols cdef int ncols @@ -2018,7 +3566,14 @@ cdef class Model: return [Column.create(cols[i]) for i in range(ncols)] def getLPRowsData(self): - """Retrieve current LP rows""" + """ + Retrieve current LP rows. + + Returns + ------- + list of Row + + """ cdef SCIP_ROW** rows cdef int nrows @@ -2026,15 +3581,37 @@ cdef class Model: return [Row.create(rows[i]) for i in range(nrows)] def getNLPRows(self): - """Retrieve the number of rows currently in the LP""" + """ + Retrieve the number of rows currently in the LP. + + Returns + ------- + int + + """ return SCIPgetNLPRows(self._scip) def getNLPCols(self): - """Retrieve the number of cols currently in the LP""" + """ + Retrieve the number of columns currently in the LP. + + Returns + ------- + int + + """ return SCIPgetNLPCols(self._scip) def getLPBasisInd(self): - """Gets all indices of basic columns and rows: index i >= 0 corresponds to column i, index i < 0 to row -i-1""" + """ + Gets all indices of basic columns and rows: + index i >= 0 corresponds to column i, index i < 0 to row -i-1 + + Returns + ------- + list of int + + """ cdef int nrows = SCIPgetNLPRows(self._scip) cdef int* inds = malloc(nrows * sizeof(int)) @@ -2044,7 +3621,19 @@ cdef class Model: return result def getLPBInvRow(self, row): - """gets a row from the inverse basis matrix B^-1""" + """ + Gets a row from the inverse basis matrix B^-1 + + Parameters + ---------- + row : int + The row index of the inverse basis matrix + + Returns + ------- + list of float + + """ # TODO: sparsity information cdef int nrows = SCIPgetNLPRows(self._scip) cdef SCIP_Real* coefs = malloc(nrows * sizeof(SCIP_Real)) @@ -2055,7 +3644,19 @@ cdef class Model: return result def getLPBInvARow(self, row): - """gets a row from B^-1 * A""" + """ + Gets a row from B^-1 * A. + + Parameters + ---------- + row : int + The row index of the inverse basis matrix multiplied by the coefficient matrix + + Returns + ------- + list of float + + """ # TODO: sparsity information cdef int ncols = SCIPgetNLPCols(self._scip) cdef SCIP_Real* coefs = malloc(ncols * sizeof(SCIP_Real)) @@ -2066,35 +3667,72 @@ cdef class Model: return result def isLPSolBasic(self): - """returns whether the current LP solution is basic, i.e. is defined by a valid simplex basis""" + """ + Returns whether the current LP solution is basic, i.e. is defined by a valid simplex basis. + + Returns + ------- + bool + + """ return SCIPisLPSolBasic(self._scip) def allColsInLP(self): - """checks if all columns, i.e. every variable with non-empty column is present in the LP. - This is not True when performing pricing for instance.""" + """ + Checks if all columns, i.e. every variable with non-empty column is present in the LP. + This is not True when performing pricing for instance. + + Returns + ------- + bool + + """ return SCIPallColsInLP(self._scip) # LP Col Methods def getColRedCost(self, Column col): - """gets the reduced cost of the column in the current LP + """ + Gets the reduced cost of the column in the current LP. + + Parameters + ---------- + col : Column + + Returns + ------- + float - :param Column col: the column of the LP for which the reduced cost will be retrieved """ return SCIPgetColRedcost(self._scip, col.scip_col) #TODO: documentation!! # LP Row Methods def createEmptyRowSepa(self, Sepa sepa, name="row", lhs = 0.0, rhs = None, local = True, modifiable = False, removable = True): - """creates and captures an LP row without any coefficients from a separator + """ + Creates and captures an LP row without any coefficients from a separator. + + Parameters + ---------- + sepa : Sepa + separator that creates the row + name : str, optional + name of row (Default value = "row") + lhs : float or None, optional + left hand side of row (Default value = 0) + rhs : float or None, optional + right hand side of row (Default value = None) + local : bool, optional + is row only valid locally? (Default value = True) + modifiable : bool, optional + is row modifiable during node processing (subject to column generation)? (Default value = False) + removable : bool, optional + should the row be removed from the LP due to aging or cleanup? (Default value = True) + + Returns + ------- + Row - :param sepa: separator that creates the row - :param name: name of row (Default value = "row") - :param lhs: left hand side of row (Default value = 0) - :param rhs: right hand side of row (Default value = None) - :param local: is row only valid locally? (Default value = True) - :param modifiable: is row modifiable during node processing (subject to column generation)? (Default value = False) - :param removable: should the row be removed from the LP due to aging or cleanup? (Default value = True) """ cdef SCIP_ROW* row lhs = -SCIPinfinity(self._scip) if lhs is None else lhs @@ -2105,14 +3743,28 @@ cdef class Model: return PyRow def createEmptyRowUnspec(self, name="row", lhs = 0.0, rhs = None, local = True, modifiable = False, removable = True): - """creates and captures an LP row without any coefficients from an unspecified source + """ + Creates and captures an LP row without any coefficients from an unspecified source. + + Parameters + ---------- + name : str, optional + name of row (Default value = "row") + lhs : float or None, optional + left hand side of row (Default value = 0) + rhs : float or None, optional + right hand side of row (Default value = None) + local : bool, optional + is row only valid locally? (Default value = True) + modifiable : bool, optional + is row modifiable during node processing (subject to column generation)? (Default value = False) + removable : bool, optional + should the row be removed from the LP due to aging or cleanup? (Default value = True) + + Returns + ------- + Row - :param name: name of row (Default value = "row") - :param lhs: left hand side of row (Default value = 0) - :param rhs: right hand side of row (Default value = None) - :param local: is row only valid locally? (Default value = True) - :param modifiable: is row modifiable during node processing (subject to column generation)? (Default value = False) - :param removable: should the row be removed from the LP due to aging or cleanup? (Default value = True) """ cdef SCIP_ROW* row lhs = -SCIPinfinity(self._scip) if lhs is None else lhs @@ -2121,108 +3773,313 @@ cdef class Model: PyRow = Row.create(row) return PyRow - def getRowActivity(self, Row row): - """returns the activity of a row in the last LP or pseudo solution""" - return SCIPgetRowActivity(self._scip, row.scip_row) + def getRowActivity(self, Row row): + """ + Returns the activity of a row in the last LP or pseudo solution. + + Parameters + ---------- + row : Row + + Returns + ------- + float + + """ + return SCIPgetRowActivity(self._scip, row.scip_row) + + def getRowLPActivity(self, Row row): + """ + Returns the activity of a row in the last LP solution. + + Parameters + ---------- + row : Row + + Returns + ------- + float - def getRowLPActivity(self, Row row): - """returns the activity of a row in the last LP solution""" + """ return SCIPgetRowLPActivity(self._scip, row.scip_row) # TODO: do we need this? (also do we need release var??) def releaseRow(self, Row row not None): - """decreases usage counter of LP row, and frees memory if necessary""" + """ + Decreases usage counter of LP row, and frees memory if necessary. + + Parameters + ---------- + row : Row + + """ PY_SCIP_CALL(SCIPreleaseRow(self._scip, &row.scip_row)) def cacheRowExtensions(self, Row row not None): - """informs row, that all subsequent additions of variables to the row should be cached and not directly applied; + """ + Informs row that all subsequent additions of variables to the row + should be cached and not directly applied; after all additions were applied, flushRowExtensions() must be called; - while the caching of row extensions is activated, information methods of the row give invalid results; - caching should be used, if a row is build with addVarToRow() calls variable by variable to increase the performance""" + while the caching of row extensions is activated, information methods of the + row give invalid results; caching should be used, if a row is build with addVarToRow() + calls variable by variable to increase the performance. + + Parameters + ---------- + row : Row + + """ PY_SCIP_CALL(SCIPcacheRowExtensions(self._scip, row.scip_row)) def flushRowExtensions(self, Row row not None): - """flushes all cached row extensions after a call of cacheRowExtensions() and merges coefficients with equal columns into a single coefficient""" + """ + Flushes all cached row extensions after a call of cacheRowExtensions() + and merges coefficients with equal columns into a single coefficient + + Parameters + ---------- + row : Row + + """ PY_SCIP_CALL(SCIPflushRowExtensions(self._scip, row.scip_row)) def addVarToRow(self, Row row not None, Variable var not None, value): - """resolves variable to columns and adds them with the coefficient to the row""" + """ + Resolves variable to columns and adds them with the coefficient to the row. + + Parameters + ---------- + row : Row + Row in which the variable will be added + var : Variable + Variable which will be added to the row + value : float + Coefficient on the variable when placed in the row + + """ PY_SCIP_CALL(SCIPaddVarToRow(self._scip, row.scip_row, var.scip_var, value)) def printRow(self, Row row not None): - """Prints row.""" + """ + Prints row. + + Parameters + ---------- + row : Row + + """ PY_SCIP_CALL(SCIPprintRow(self._scip, row.scip_row, NULL)) def getRowNumIntCols(self, Row row): - """Returns number of intergal columns in the row""" + """ + Returns number of intergal columns in the row. + + Parameters + ---------- + row : Row + + Returns + ------- + int + + """ return SCIPgetRowNumIntCols(self._scip, row.scip_row) def getRowObjParallelism(self, Row row): - """Returns 1 if the row is parallel, and 0 if orthogonal""" + """ + Returns 1 if the row is parallel, and 0 if orthogonal. + + Parameters + ---------- + row : Row + + Returns + ------- + float + + """ return SCIPgetRowObjParallelism(self._scip, row.scip_row) def getRowParallelism(self, Row row1, Row row2, orthofunc=101): - """Returns the degree of parallelism between hyplerplanes. 1 if perfectly parallel, 0 if orthogonal. - For two row vectors v, w the parallelism is calculated as: |v*w|/(|v|*|w|). - 101 in this case is an 'e' (euclidean) in ASCII. The other acceptable input is 100 (d for discrete).""" + """ + Returns the degree of parallelism between hyplerplanes. 1 if perfectly parallel, 0 if orthogonal. + For two row vectors v, w the parallelism is calculated as: abs(v*w)/(abs(v)*abs(w)). + 101 in this case is an 'e' (euclidean) in ASCII. The other acceptable input is 100 (d for discrete). + + Parameters + ---------- + row1 : Row + row2 : Row + orthofunc : int, optional + 101 (default) is an 'e' (euclidean) in ASCII. Alternate value is 100 (d for discrete) + + Returns + ------- + float + + """ return SCIProwGetParallelism(row1.scip_row, row2.scip_row, orthofunc) def getRowDualSol(self, Row row): - """Gets the dual LP solution of a row""" + """ + Gets the dual LP solution of a row. + + Parameters + ---------- + row : Row + + Returns + ------- + float + + """ return SCIProwGetDualsol(row.scip_row) # Cutting Plane Methods def addPoolCut(self, Row row not None): - """if not already existing, adds row to global cut pool""" + """ + If not already existing, adds row to global cut pool. + + Parameters + ---------- + row : Row + + """ PY_SCIP_CALL(SCIPaddPoolCut(self._scip, row.scip_row)) def getCutEfficacy(self, Row cut not None, Solution sol = None): - """returns efficacy of the cut with respect to the given primal solution or the current LP solution: e = -feasibility/norm""" + """ + Returns efficacy of the cut with respect to the given primal solution or the + current LP solution: e = -feasibility/norm + + Parameters + ---------- + cut : Row + sol : Solution or None, optional + + Returns + ------- + float + + """ return SCIPgetCutEfficacy(self._scip, NULL if sol is None else sol.sol, cut.scip_row) def isCutEfficacious(self, Row cut not None, Solution sol = None): - """ returns whether the cut's efficacy with respect to the given primal solution or the current LP solution is greater than the minimal cut efficacy""" + """ + Returns whether the cut's efficacy with respect to the given primal solution or the + current LP solution is greater than the minimal cut efficacy. + + Parameters + ---------- + cut : Row + sol : Solution or None, optional + + Returns + ------- + float + + """ return SCIPisCutEfficacious(self._scip, NULL if sol is None else sol.sol, cut.scip_row) def getCutLPSolCutoffDistance(self, Row cut not None, Solution sol not None): - """ returns row's cutoff distance in the direction of the given primal solution""" + """ + Returns row's cutoff distance in the direction of the given primal solution. + + Parameters + ---------- + cut : Row + sol : Solution + + Returns + ------- + float + + """ return SCIPgetCutLPSolCutoffDistance(self._scip, sol.sol, cut.scip_row) def addCut(self, Row cut not None, forcecut = False): - """adds cut to separation storage and returns whether cut has been detected to be infeasible for local bounds""" + """ + Adds cut to separation storage and returns whether cut has been detected to be infeasible for local bounds. + + Parameters + ---------- + cut : Row + The cut that will be added + forcecut : bool, optional + Whether the cut should be forced or not, i.e., selected no matter what + + Returns + ------- + infeasible : bool + Whether the cut has been detected to be infeasible from local bounds + + """ cdef SCIP_Bool infeasible PY_SCIP_CALL(SCIPaddRow(self._scip, cut.scip_row, forcecut, &infeasible)) return infeasible def getNCuts(self): - """Retrieve total number of cuts in storage""" + """ + Retrieve total number of cuts in storage. + + Returns + ------- + int + + """ return SCIPgetNCuts(self._scip) def getNCutsApplied(self): - """Retrieve number of currently applied cuts""" + """ + Retrieve number of currently applied cuts. + + Returns + ------- + int + + """ return SCIPgetNCutsApplied(self._scip) def getNSepaRounds(self): - """Retrieve the number of separation rounds that have been performed - at the current node""" + """ + Retrieve the number of separation rounds that have been performed + at the current node. + + Returns + ------- + int + + """ return SCIPgetNSepaRounds(self._scip) def separateSol(self, Solution sol = None, pretendroot = False, allowlocal = True, onlydelayed = False): - """separates the given primal solution or the current LP solution by calling the separators and constraint handlers' - separation methods; - the generated cuts are stored in the separation storage and can be accessed with the methods SCIPgetCuts() and - SCIPgetNCuts(); + """ + Separates the given primal solution or the current LP solution by calling + the separators and constraint handlers' separation methods; + the generated cuts are stored in the separation storage and can be accessed + with the methods SCIPgetCuts() and SCIPgetNCuts(); after evaluating the cuts, you have to call SCIPclearCuts() in order to remove the cuts from the - separation storage; - it is possible to call SCIPseparateSol() multiple times with different solutions and evaluate the found cuts - afterwards - :param Solution sol: solution to separate, None to use current lp solution (Default value = None) - :param pretendroot: should the cut separators be called as if we are at the root node? (Default value = "False") - :param allowlocal: should the separator be asked to separate local cuts (Default value = True) - :param onlydelayed: should only separators be called that were delayed in the previous round? (Default value = False) - returns - delayed -- whether a separator was delayed - cutoff -- whether the node can be cut off + separation storage; it is possible to call SCIPseparateSol() multiple times with + different solutions and evaluate the found cuts afterwards. + + Parameters + ---------- + sol : Solution or None, optional + solution to separate, None to use current lp solution (Default value = None) + pretendroot : bool, optional + should the cut separators be called as if we are at the root node? (Default value = "False") + allowlocal : bool, optional + should the separator be asked to separate local cuts (Default value = True) + onlydelayed : bool, optional + should only separators be called that were delayed in the previous round? (Default value = False) + + Returns + ------- + delayed : bool + whether a separator was delayed + cutoff : bool + whether the node can be cut off + """ cdef SCIP_Bool delayed cdef SCIP_Bool cutoff @@ -2231,6 +4088,20 @@ cdef class Model: return delayed, cutoff def _createConsLinear(self, ExprCons lincons, **kwargs): + """ + The function for creating a linear constraint, but not adding it to the Model. + Please do not use this function directly, but rather use createConsFromExpr + + Parameters + ---------- + lincons : ExprCons + kwargs : dict, optional + + Returns + ------- + Constraint + + """ assert isinstance(lincons, ExprCons), "given constraint is not ExprCons but %s" % lincons.__class__.__name__ assert lincons.expr.degree() <= 1, "given constraint is not linear, degree == %d" % lincons.expr.degree() @@ -2261,6 +4132,20 @@ cdef class Model: return PyCons def _createConsQuadratic(self, ExprCons quadcons, **kwargs): + """ + The function for creating a quadratic constraint, but not adding it to the Model. + Please do not use this function directly, but rather use createConsFromExpr + + Parameters + ---------- + quadcons : ExprCons + kwargs : dict, optional + + Returns + ------- + Constraint + + """ terms = quadcons.expr.terms assert quadcons.expr.degree() <= 2, "given constraint is not quadratic, degree == %d" % quadcons.expr.degree() @@ -2300,6 +4185,20 @@ cdef class Model: return PyCons def _createConsNonlinear(self, cons, **kwargs): + """ + The function for creating a non-linear constraint, but not adding it to the Model. + Please do not use this function directly, but rather use createConsFromExpr + + Parameters + ---------- + cons : ExprCons + kwargs : dict, optional + + Returns + ------- + Constraint + + """ cdef SCIP_EXPR* expr cdef SCIP_EXPR** varexprs cdef SCIP_EXPR** monomials @@ -2355,6 +4254,20 @@ cdef class Model: return PyCons def _createConsGenNonlinear(self, cons, **kwargs): + """ + The function for creating a general non-linear constraint, but not adding it to the Model. + Please do not use this function directly, but rather use createConsFromExpr + + Parameters + ---------- + cons : ExprCons + kwargs : dict, optional + + Returns + ------- + Constraint + + """ cdef SCIP_EXPR** childrenexpr cdef SCIP_EXPR** scipexprs cdef SCIP_CONS* scip_cons @@ -2479,23 +4392,44 @@ cdef class Model: enforce=True, check=True, propagate=True, local=False, modifiable=False, dynamic=False, removable=False, stickingatnode=False): - """Create a linear or nonlinear constraint without adding it to the SCIP problem. This is useful for creating disjunction constraints - without also enforcing the individual constituents. Currently, this can only be used as an argument to `.addConsElemDisjunction`. To add + """ + Create a linear or nonlinear constraint without adding it to the SCIP problem. + This is useful for creating disjunction constraints without also enforcing the individual constituents. + Currently, this can only be used as an argument to `.addConsElemDisjunction`. To add an individual linear/nonlinear constraint, prefer `.addCons()`. - :param cons: constraint object - :param name: the name of the constraint, generic name if empty (Default value = '') - :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True) - :param separate: should the constraint be separated during LP processing? (Default value = True) - :param enforce: should the constraint be enforced during node processing? (Default value = True) - :param check: should the constraint be checked for feasibility? (Default value = True) - :param propagate: should the constraint be propagated during node processing? (Default value = True) - :param local: is the constraint only valid locally? (Default value = False) - :param modifiable: is the constraint modifiable (subject to column generation)? (Default value = False) - :param dynamic: is the constraint subject to aging? (Default value = False) - :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) - :param stickingatnode: should the constraint always be kept at the node where it was added, even if it may be moved to a more global node? (Default value = False) - :return The created @ref scip#Constraint "Constraint" object. + Parameters + ---------- + cons : ExprCons + The expression constraint that is not yet an actual constraint + name : str, optional + the name of the constraint, generic name if empty (Default value = '') + initial : bool, optional + should the LP relaxation of constraint be in the initial LP? (Default value = True) + separate : bool, optional + should the constraint be separated during LP processing? (Default value = True) + enforce : bool, optional + should the constraint be enforced during node processing? (Default value = True) + check : bool, optional + should the constraint be checked for feasibility? (Default value = True) + propagate : bool, optional + should the constraint be propagated during node processing? (Default value = True) + local : bool, optional + is the constraint only valid locally? (Default value = False) + modifiable : bool, optional + is the constraint modifiable (subject to column generation)? (Default value = False) + dynamic : bool, optional + is the constraint subject to aging? (Default value = False) + removable : bool, optional + should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) + stickingatnode : bool, optional + should the constraint always be kept at the node where it was added, + even if it may be moved to a more global node? (Default value = False) + + Returns + ------- + Constraint + The created Constraint object. """ if name == '': @@ -2525,21 +4459,41 @@ cdef class Model: enforce=True, check=True, propagate=True, local=False, modifiable=False, dynamic=False, removable=False, stickingatnode=False): - """Add a linear or nonlinear constraint. + """ + Add a linear or nonlinear constraint. - :param cons: constraint object - :param name: the name of the constraint, generic name if empty (Default value = '') - :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True) - :param separate: should the constraint be separated during LP processing? (Default value = True) - :param enforce: should the constraint be enforced during node processing? (Default value = True) - :param check: should the constraint be checked for feasibility? (Default value = True) - :param propagate: should the constraint be propagated during node processing? (Default value = True) - :param local: is the constraint only valid locally? (Default value = False) - :param modifiable: is the constraint modifiable (subject to column generation)? (Default value = False) - :param dynamic: is the constraint subject to aging? (Default value = False) - :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) - :param stickingatnode: should the constraint always be kept at the node where it was added, even if it may be moved to a more global node? (Default value = False) - :return The added @ref scip#Constraint "Constraint" object. + Parameters + ---------- + cons : ExprCons + The expression constraint that is not yet an actual constraint + name : str, optional + the name of the constraint, generic name if empty (Default value = "") + initial : bool, optional + should the LP relaxation of constraint be in the initial LP? (Default value = True) + separate : bool, optional + should the constraint be separated during LP processing? (Default value = True) + enforce : bool, optional + should the constraint be enforced during node processing? (Default value = True) + check : bool, optional + should the constraint be checked for feasibility? (Default value = True) + propagate : bool, optional + should the constraint be propagated during node processing? (Default value = True) + local : bool, optional + is the constraint only valid locally? (Default value = False) + modifiable : bool, optional + is the constraint modifiable (subject to column generation)? (Default value = False) + dynamic : bool, optional + is the constraint subject to aging? (Default value = False) + removable : bool, optional + should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) + stickingatnode : bool, optional + should the constraints always be kept at the node where it was added, + even if it may be moved to a more global node? (Default value = False) + + Returns + ------- + Constraint + The created and added Constraint object. """ assert isinstance(cons, ExprCons), "given constraint is not ExprCons but %s" % cons.__class__.__name__ @@ -2573,25 +4527,45 @@ cdef class Model: Each of the constraints is added to the model using Model.addCons(). - For all parameters, except @p conss, this method behaves differently depending on the type of the passed argument: - 1. If the value is iterable, it must be of the same length as @p conss. For each constraint, Model.addCons() will be called with the value at the corresponding index. - 2. Else, the (default) value will be applied to all of the constraints. - - :param conss An iterable of constraint objects. Any iterable will be converted into a list before further processing. - :param name: the names of the constraints, generic name if empty (Default value = ''). If a single string is passed, it will be suffixed by an underscore and the enumerated index of the constraint (starting with 0). - :param initial: should the LP relaxation of constraints be in the initial LP? (Default value = True) - :param separate: should the constraints be separated during LP processing? (Default value = True) - :param enforce: should the constraints be enforced during node processing? (Default value = True) - :param check: should the constraints be checked for feasibility? (Default value = True) - :param propagate: should the constraints be propagated during node processing? (Default value = True) - :param local: are the constraints only valid locally? (Default value = False) - :param modifiable: are the constraints modifiable (subject to column generation)? (Default value = False) - :param dynamic: are the constraints subject to aging? (Default value = False) - :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) - :param stickingatnode: should the constraints always be kept at the node where it was added, even if it may be @oved to a more global node? (Default value = False) - :return A list of added @ref scip#Constraint "Constraint" objects. + For all parameters, except `conss`, this method behaves differently depending on the + type of the passed argument: + 1. If the value is iterable, it must be of the same length as `conss`. For each + constraint, Model.addCons() will be called with the value at the corresponding index. + 2. Else, the (default) value will be applied to all of the constraints. + + Parameters + ---------- + conss : iterable of ExprCons + An iterable of constraint objects. Any iterable will be converted into a list before further processing. + name : str or iterable of str, optional + the name of the constraint, generic name if empty (Default value = '') + initial : bool or iterable of bool, optional + should the LP relaxation of constraint be in the initial LP? (Default value = True) + separate : bool or iterable of bool, optional + should the constraint be separated during LP processing? (Default value = True) + enforce : bool or iterable of bool, optional + should the constraint be enforced during node processing? (Default value = True) + check : bool or iterable of bool, optional + should the constraint be checked for feasibility? (Default value = True) + propagate : bool or iterable of bool, optional + should the constraint be propagated during node processing? (Default value = True) + local : bool or iterable of bool, optional + is the constraint only valid locally? (Default value = False) + modifiable : bool or iterable of bool, optional + is the constraint modifiable (subject to column generation)? (Default value = False) + dynamic : bool or iterable of bool, optional + is the constraint subject to aging? (Default value = False) + removable : bool or iterable of bool, optional + should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) + stickingatnode : bool or iterable of bool, optional + should the constraints always be kept at the node where it was added, + even if it may be moved to a more global node? (Default value = False) + + Returns + ------- + list of Constraint + The created and added Constraint objects. - :see addCons() """ def ensure_iterable(elem, length): if isinstance(elem, Iterable): @@ -2633,18 +4607,37 @@ cdef class Model: def addConsDisjunction(self, conss, name = '', initial = True, relaxcons = None, enforce=True, check =True, local=False, modifiable = False, dynamic = False): - """Add a disjunction constraint. + """ + Add a disjunction constraint. + + Parameters + ---------- + conss : iterable of ExprCons + An iterable of constraint objects to be included initially in the disjunction. + Currently, these must be expressions. + name : str, optional + the name of the disjunction constraint. + initial : bool, optional + should the LP relaxation of disjunction constraint be in the initial LP? (Default value = True) + relaxcons : None, optional + a conjunction constraint containing the linear relaxation of the disjunction constraint, or None. + NOT YET SUPPORTED. (Default value = None) + enforce : bool, optional + should the constraint be enforced during node processing? (Default value = True) + check : bool, optional + should the constraint be checked for feasibility? (Default value = True) + local : bool, optional + is the constraint only valid locally? (Default value = False) + modifiable : bool, optional + is the constraint modifiable (subject to column generation)? (Default value = False) + dynamic : bool, optional + is the constraint subject to aging? (Default value = False) + + Returns + ------- + Constraint + The created disjunction constraint - :param Iterable[Constraint] conss: An iterable of constraint objects to be included initially in the disjunction. Currently, these must be expressions. - :param name: the name of the disjunction constraint. - :param initial: should the LP relaxation of disjunction constraint be in the initial LP? (Default value = True) - :param relaxcons: a conjunction constraint containing the linear relaxation of the disjunction constraint, or None. (Default value = None) - :param enforce: should the constraint be enforced during node processing? (Default value = True) - :param check: should the constraint be checked for feasibility? (Default value = True) - :param local: is the constraint only valid locally? (Default value = False) - :param modifiable: is the constraint modifiable (subject to column generation)? (Default value = False) - :param dynamic: is the constraint subject to aging? (Default value = False) - :return The added @ref scip#Constraint "Constraint" object. """ def ensure_iterable(elem, length): if isinstance(elem, Iterable): @@ -2682,11 +4675,21 @@ cdef class Model: return PyCons def addConsElemDisjunction(self, Constraint disj_cons, Constraint cons): - """Appends a constraint to a disjunction. + """ + Appends a constraint to a disjunction. + + Parameters + ---------- + disj_cons : Constraint + the disjunction constraint to append to. + cons : Constraint + the constraint to append + + Returns + ------- + disj_cons : Constraint + The disjunction constraint with `cons` appended. - :param Constraint disj_cons: the disjunction constraint to append to. - :param Constraint cons: the Constraint to append - :return The disjunction constraint with added @ref scip#Constraint object. """ PY_SCIP_CALL(SCIPaddConsElemDisjunction(self._scip, (disj_cons).scip_cons, (cons).scip_cons)) PY_SCIP_CALL(SCIPreleaseCons(self._scip, &(cons).scip_cons)) @@ -2696,7 +4699,20 @@ cdef class Model: """ Gets number of variables in a constraint. - :param constraint: Constraint to get the number of variables from. + Parameters + ---------- + constraint : Constraint + Constraint to get the number of variables from. + + Returns + ------- + int + + Raises + ------ + TypeError + If the associated constraint handler does not have this functionality + """ cdef int nvars cdef SCIP_Bool success @@ -2714,7 +4730,15 @@ cdef class Model: """ Gets variables in a constraint. - :param constraint: Constraint to get the variables from. + Parameters + ---------- + constraint : Constraint + Constraint to get the variables from. + + Returns + ------- + list of Variable + """ cdef SCIP_Bool success cdef int _nvars @@ -2739,13 +4763,26 @@ cdef class Model: return vars def printCons(self, Constraint constraint): + """ + Print the constraint + + Parameters + ---------- + constraint : Constraint + + """ return PY_SCIP_CALL(SCIPprintCons(self._scip, constraint.scip_cons, NULL)) - # TODO Find a better way to retrieve a scip expression from a python expression. Consider making GenExpr include Expr, to avoid using Union. See PR #760. - from typing import Union - def addExprNonlinear(self, Constraint cons, expr: Union[Expr,GenExpr], float coef): + def addExprNonlinear(self, Constraint cons, expr, coef): """ Add coef*expr to nonlinear constraint. + + Parameters + ---------- + cons : Constraint + expr : Expr or GenExpr + coef : float + """ assert self.getStage() == 1, "addExprNonlinear cannot be called in stage %i." % self.getStage() assert cons.isNonlinear(), "addExprNonlinear can only be called with nonlinear constraints." @@ -2760,21 +4797,33 @@ cdef class Model: self.delCons(temp_cons) def addConsCoeff(self, Constraint cons, Variable var, coeff): - """Add coefficient to the linear constraint (if non-zero). + """ + Add coefficient to the linear constraint (if non-zero). - :param Constraint cons: constraint to be changed - :param Variable var: variable to be added - :param coeff: coefficient of new variable + Parameters + ---------- + cons : Constraint + Constraint to be changed + var : Variable + variable to be added + coeff : float + coefficient of new variable """ PY_SCIP_CALL(SCIPaddCoefLinear(self._scip, cons.scip_cons, var.scip_var, coeff)) def addConsNode(self, Node node, Constraint cons, Node validnode=None): - """Add a constraint to the given node + """ + Add a constraint to the given node. - :param Node node: node to add the constraint to - :param Constraint cons: constraint to add - :param Node validnode: more global node where cons is also valid + Parameters + ---------- + node : Node + node at which the constraint will be added + cons : Constraint + the constraint to add to the node + validnode : Node or None, optional + more global node where cons is also valid. (Default=None) """ if isinstance(validnode, Node): @@ -2784,10 +4833,15 @@ cdef class Model: Py_INCREF(cons) def addConsLocal(self, Constraint cons, Node validnode=None): - """Add a constraint to the current node + """ + Add a constraint to the current node. - :param Constraint cons: constraint to add - :param Node validnode: more global node where cons is also valid + Parameters + ---------- + cons : Constraint + the constraint to add to the current node + validnode : Node or None, optional + more global node where cons is also valid. (Default=None) """ if isinstance(validnode, Node): @@ -2800,7 +4854,8 @@ cdef class Model: initial=True, separate=True, enforce=True, check=True, propagate=True, local=False, dynamic=False, removable=False, stickingatnode=False): - """Add an SOS1 constraint. + """ + Add an SOS1 constraint. :param vars: list of variables to be included :param weights: list of weights (Default value = None) @@ -2815,6 +4870,40 @@ cdef class Model: :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) :param stickingatnode: should the constraint always be kept at the node where it was added, even if it may be moved to a more global node? (Default value = False) + + Parameters + ---------- + vars : list of Variable + list of variables to be included + weights : list of float or None, optional + list of weights (Default value = None) + name : str, optional + name of the constraint (Default value = "SOS1cons") + initial : bool, optional + should the LP relaxation of constraint be in the initial LP? (Default value = True) + separate : bool, optional + should the constraint be separated during LP processing? (Default value = True) + enforce : bool, optional + should the constraint be enforced during node processing? (Default value = True) + check : bool, optional + should the constraint be checked for feasibility? (Default value = True) + propagate : bool, optional + should the constraint be propagated during node processing? (Default value = True) + local : bool, optional + is the constraint only valid locally? (Default value = False) + dynamic : bool, optional + is the constraint subject to aging? (Default value = False) + removable : bool, optional + should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) + stickingatnode : bool, optional + should the constraint always be kept at the node where it was added, + even if it may be moved to a more global node? (Default value = False) + + Returns + ------- + Constraint + The newly created SOS1 constraint + """ cdef SCIP_CONS* scip_cons cdef int _nvars @@ -2839,20 +4928,41 @@ cdef class Model: initial=True, separate=True, enforce=True, check=True, propagate=True, local=False, dynamic=False, removable=False, stickingatnode=False): - """Add an SOS2 constraint. + """ + Add an SOS2 constraint. - :param vars: list of variables to be included - :param weights: list of weights (Default value = None) - :param name: name of the constraint (Default value = "SOS2cons") - :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True) - :param separate: should the constraint be separated during LP processing? (Default value = True) - :param enforce: should the constraint be enforced during node processing? (Default value = True) - :param check: should the constraint be checked for feasibility? (Default value = True) - :param propagate: is the constraint only valid locally? (Default value = True) - :param local: is the constraint only valid locally? (Default value = False) - :param dynamic: is the constraint subject to aging? (Default value = False) - :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) - :param stickingatnode: should the constraint always be kept at the node where it was added, even if it may be moved to a more global node? (Default value = False) + Parameters + ---------- + vars : list of Variable + list of variables to be included + weights : list of float or None, optional + list of weights (Default value = None) + name : str, optional + name of the constraint (Default value = "SOS2cons") + initial : bool, optional + should the LP relaxation of constraint be in the initial LP? (Default value = True) + separate : bool, optional + should the constraint be separated during LP processing? (Default value = True) + enforce : bool, optional + should the constraint be enforced during node processing? (Default value = True) + check : bool, optional + should the constraint be checked for feasibility? (Default value = True) + propagate : bool, optional + should the constraint be propagated during node processing? (Default value = True) + local : bool, optional + is the constraint only valid locally? (Default value = False) + dynamic : bool, optional + is the constraint subject to aging? (Default value = False) + removable : bool, optional + should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) + stickingatnode : bool, optional + should the constraint always be kept at the node where it was added, + even if it may be moved to a more global node? (Default value = False) + + Returns + ------- + Constraint + The newly created SOS2 constraint """ cdef SCIP_CONS* scip_cons @@ -2878,20 +4988,42 @@ cdef class Model: initial=True, separate=True, enforce=True, check=True, propagate=True, local=False, modifiable=False, dynamic=False, removable=False, stickingatnode=False): - """Add an AND-constraint. - :param vars: list of BINARY variables to be included (operators) - :param resvar: BINARY variable (resultant) - :param name: name of the constraint (Default value = "ANDcons") - :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True) - :param separate: should the constraint be separated during LP processing? (Default value = True) - :param enforce: should the constraint be enforced during node processing? (Default value = True) - :param check: should the constraint be checked for feasibility? (Default value = True) - :param propagate: should the constraint be propagated during node processing? (Default value = True) - :param local: is the constraint only valid locally? (Default value = False) - :param modifiable: is the constraint modifiable (subject to column generation)? (Default value = False) - :param dynamic: is the constraint subject to aging? (Default value = False) - :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) - :param stickingatnode: should the constraint always be kept at the node where it was added, even if it may be moved to a more global node? (Default value = False) + """ + Add an AND-constraint. + + Parameters + ---------- + vars : list of Variable + list of BINARY variables to be included (operators) + resvar : Variable + BINARY variable (resultant) + name : str, optional + name of the constraint (Default value = "ANDcons") + initial : bool, optional + should the LP relaxation of constraint be in the initial LP? (Default value = True) + separate : bool, optional + should the constraint be separated during LP processing? (Default value = True) + enforce : bool, optional + should the constraint be enforced during node processing? (Default value = True) + check : bool, optional + should the constraint be checked for feasibility? (Default value = True) + propagate : bool, optional + should the constraint be propagated during node processing? (Default value = True) + local : bool, optional + is the constraint only valid locally? (Default value = False) + dynamic : bool, optional + is the constraint subject to aging? (Default value = False) + removable : bool, optional + should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) + stickingatnode : bool, optional + should the constraint always be kept at the node where it was added, + even if it may be moved to a more global node? (Default value = False) + + Returns + ------- + Constraint + The newly created AND constraint + """ cdef SCIP_CONS* scip_cons @@ -2913,24 +5045,46 @@ cdef class Model: return pyCons - def addConsOr(self, vars, resvar, name="ORcons", - initial=True, separate=True, enforce=True, check=True, - propagate=True, local=False, modifiable=False, dynamic=False, - removable=False, stickingatnode=False): - """Add an OR-constraint. - :param vars: list of BINARY variables to be included (operators) - :param resvar: BINARY variable (resultant) - :param name: name of the constraint (Default value = "ORcons") - :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True) - :param separate: should the constraint be separated during LP processing? (Default value = True) - :param enforce: should the constraint be enforced during node processing? (Default value = True) - :param check: should the constraint be checked for feasibility? (Default value = True) - :param propagate: should the constraint be propagated during node processing? (Default value = True) - :param local: is the constraint only valid locally? (Default value = False) - :param modifiable: is the constraint modifiable (subject to column generation)? (Default value = False) - :param dynamic: is the constraint subject to aging? (Default value = False) - :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) - :param stickingatnode: should the constraint always be kept at the node where it was added, even if it may be moved to a more global node? (Default value = False) + def addConsOr(self, vars, resvar, name="ORcons", + initial=True, separate=True, enforce=True, check=True, + propagate=True, local=False, modifiable=False, dynamic=False, + removable=False, stickingatnode=False): + """ + Add an OR-constraint. + + Parameters + ---------- + vars : list of Variable + list of BINARY variables to be included (operators) + resvar : Variable + BINARY variable (resultant) + name : str, optional + name of the constraint (Default value = "ORcons") + initial : bool, optional + should the LP relaxation of constraint be in the initial LP? (Default value = True) + separate : bool, optional + should the constraint be separated during LP processing? (Default value = True) + enforce : bool, optional + should the constraint be enforced during node processing? (Default value = True) + check : bool, optional + should the constraint be checked for feasibility? (Default value = True) + propagate : bool, optional + should the constraint be propagated during node processing? (Default value = True) + local : bool, optional + is the constraint only valid locally? (Default value = False) + dynamic : bool, optional + is the constraint subject to aging? (Default value = False) + removable : bool, optional + should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) + stickingatnode : bool, optional + should the constraint always be kept at the node where it was added, + even if it may be moved to a more global node? (Default value = False) + + Returns + ------- + Constraint + The newly created OR constraint + """ cdef SCIP_CONS* scip_cons @@ -2956,20 +5110,42 @@ cdef class Model: initial=True, separate=True, enforce=True, check=True, propagate=True, local=False, modifiable=False, dynamic=False, removable=False, stickingatnode=False): - """Add a XOR-constraint. - :param vars: list of BINARY variables to be included (operators) - :param rhsvar: BOOLEAN value, explicit True, False or bool(obj) is needed (right-hand side) - :param name: name of the constraint (Default value = "XORcons") - :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True) - :param separate: should the constraint be separated during LP processing? (Default value = True) - :param enforce: should the constraint be enforced during node processing? (Default value = True) - :param check: should the constraint be checked for feasibility? (Default value = True) - :param propagate: should the constraint be propagated during node processing? (Default value = True) - :param local: is the constraint only valid locally? (Default value = False) - :param modifiable: is the constraint modifiable (subject to column generation)? (Default value = False) - :param dynamic: is the constraint subject to aging? (Default value = False) - :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) - :param stickingatnode: should the constraint always be kept at the node where it was added, even if it may be moved to a more global node? (Default value = False) + """ + Add a XOR-constraint. + + Parameters + ---------- + vars : list of Variable + list of binary variables to be included (operators) + rhsvar : bool + BOOLEAN value, explicit True, False or bool(obj) is needed (right-hand side) + name : str, optional + name of the constraint (Default value = "XORcons") + initial : bool, optional + should the LP relaxation of constraint be in the initial LP? (Default value = True) + separate : bool, optional + should the constraint be separated during LP processing? (Default value = True) + enforce : bool, optional + should the constraint be enforced during node processing? (Default value = True) + check : bool, optional + should the constraint be checked for feasibility? (Default value = True) + propagate : bool, optional + should the constraint be propagated during node processing? (Default value = True) + local : bool, optional + is the constraint only valid locally? (Default value = False) + dynamic : bool, optional + is the constraint subject to aging? (Default value = False) + removable : bool, optional + should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) + stickingatnode : bool, optional + should the constraint always be kept at the node where it was added, + even if it may be moved to a more global node? (Default value = False) + + Returns + ------- + Constraint + The newly created XOR constraint + """ cdef SCIP_CONS* scip_cons @@ -2995,22 +5171,48 @@ cdef class Model: initial=True, separate=True, enforce=True, check=True, propagate=True, local=False, dynamic=False, removable=False, stickingatnode=False): - """Add a cardinality constraint that allows at most 'cardval' many nonzero variables. + """ + Add a cardinality constraint that allows at most 'cardval' many nonzero variables. - :param consvars: list of variables to be included - :param cardval: nonnegative integer - :param indvars: indicator variables indicating which variables may be treated as nonzero in cardinality constraint, or None if new indicator variables should be introduced automatically (Default value = None) - :param weights: weights determining the variable order, or None if variables should be ordered in the same way they were added to the constraint (Default value = None) - :param name: name of the constraint (Default value = "CardinalityCons") - :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True) - :param separate: should the constraint be separated during LP processing? (Default value = True) - :param enforce: should the constraint be enforced during node processing? (Default value = True) - :param check: should the constraint be checked for feasibility? (Default value = True) - :param propagate: should the constraint be propagated during node processing? (Default value = True) - :param local: is the constraint only valid locally? (Default value = False) - :param dynamic: is the constraint subject to aging? (Default value = False) - :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) - :param stickingatnode: should the constraint always be kept at the node where it was added, even if it may be moved to a more global node? (Default value = False) + Parameters + ---------- + consvars : list of Variable + list of variables to be included + cardval : int + nonnegative integer + indvars : list of Variable or None, optional + indicator variables indicating which variables may be treated as nonzero in + cardinality constraint, or None if new indicator variables should be + introduced automatically (Default value = None) + weights : list of float or None, optional + weights determining the variable order, or None if variables should be ordered + in the same way they were added to the constraint (Default value = None) + name : str, optional + name of the constraint (Default value = "CardinalityCons") + initial : bool, optional + should the LP relaxation of constraint be in the initial LP? (Default value = True) + separate : bool, optional + should the constraint be separated during LP processing? (Default value = True) + enforce : bool, optional + should the constraint be enforced during node processing? (Default value = True) + check : bool, optional + should the constraint be checked for feasibility? (Default value = True) + propagate : bool, optional + should the constraint be propagated during node processing? (Default value = True) + local : bool, optional + is the constraint only valid locally? (Default value = False) + dynamic : bool, optional + is the constraint subject to aging? (Default value = False) + removable : bool, optional + should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) + stickingatnode : bool, optional + should the constraint always be kept at the node where it was added, + even if it may be moved to a more global node? (Default value = False) + + Returns + ------- + Constraint + The newly created Cardinality constraint """ cdef SCIP_CONS* scip_cons @@ -3045,24 +5247,45 @@ cdef class Model: initial=True, separate=True, enforce=True, check=True, propagate=True, local=False, dynamic=False, removable=False, stickingatnode=False): - """Add an indicator constraint for the linear inequality 'cons'. + """Add an indicator constraint for the linear inequality `cons`. - The 'binvar' argument models the redundancy of the linear constraint. A solution for which - 'binvar' is 1 must satisfy the constraint. + The `binvar` argument models the redundancy of the linear constraint. A solution for which + `binvar` is 1 must satisfy the constraint. - :param cons: a linear inequality of the form "<=" - :param binvar: binary indicator variable, or None if it should be created (Default value = None) - :param activeone: constraint should active if binvar is 1 (0 if activeone = False) - :param name: name of the constraint (Default value = "IndicatorCons") - :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True) - :param separate: should the constraint be separated during LP processing? (Default value = True) - :param enforce: should the constraint be enforced during node processing? (Default value = True) - :param check: should the constraint be checked for feasibility? (Default value = True) - :param propagate: should the constraint be propagated during node processing? (Default value = True) - :param local: is the constraint only valid locally? (Default value = False) - :param dynamic: is the constraint subject to aging? (Default value = False) - :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) - :param stickingatnode: should the constraint always be kept at the node where it was added, even if it may be moved to a more global node? (Default value = False) + Parameters + ---------- + cons : ExprCons + a linear inequality of the form "<=" + binvar : Variable, optional + binary indicator variable, or None if it should be created (Default value = None) + activeone : bool, optional + constraint should active if binvar is 1 (0 if activeone = False) + name : str, optional + name of the constraint (Default value = "IndicatorCons") + initial : bool, optional + should the LP relaxation of constraint be in the initial LP? (Default value = True) + separate : bool, optional + should the constraint be separated during LP processing? (Default value = True) + enforce : bool, optional + should the constraint be enforced during node processing? (Default value = True) + check : bool, optional + should the constraint be checked for feasibility? (Default value = True) + propagate : bool, optional + should the constraint be propagated during node processing? (Default value = True) + local : bool, optional + is the constraint only valid locally? (Default value = False) + dynamic : bool, optional + is the constraint subject to aging? (Default value = False) + removable : bool, optional + should the relaxation be removed from the LP due to aging or cleanup? (Default value = False) + stickingatnode : bool, optional + should the constraint always be kept at the node where it was added, + even if it may be moved to a more global node? (Default value = False) + + Returns + ------- + Constraint + The newly created Indicator constraint """ assert isinstance(cons, ExprCons), "given constraint is not ExprCons but %s" % cons.__class__.__name__ @@ -3106,102 +5329,154 @@ cdef class Model: return pyCons def getSlackVarIndicator(self, Constraint cons): - """Get slack variable of an indicator constraint. + """ + Get slack variable of an indicator constraint. + - :param Constraint cons: indicator constraint + Parameters + ---------- + cons : Constraint + The indicator constraint + + Returns + ------- + Variable """ cdef SCIP_VAR* var = SCIPgetSlackVarIndicator(cons.scip_cons); return Variable.create(var) def addPyCons(self, Constraint cons): - """Adds a customly created cons. + """ + Adds a customly created cons. - :param Constraint cons: constraint to add + Parameters + ---------- + cons : Constraint + constraint to add """ PY_SCIP_CALL(SCIPaddCons(self._scip, cons.scip_cons)) Py_INCREF(cons) def addVarSOS1(self, Constraint cons, Variable var, weight): - """Add variable to SOS1 constraint. + """ + Add variable to SOS1 constraint. - :param Constraint cons: SOS1 constraint - :param Variable var: new variable - :param weight: weight of new variable + Parameters + ---------- + cons : Constraint + SOS1 constraint + var : Variable + new variable + weight : weight + weight of new variable """ PY_SCIP_CALL(SCIPaddVarSOS1(self._scip, cons.scip_cons, var.scip_var, weight)) def appendVarSOS1(self, Constraint cons, Variable var): - """Append variable to SOS1 constraint. + """ + Append variable to SOS1 constraint. - :param Constraint cons: SOS1 constraint - :param Variable var: variable to append + Parameters + ---------- + cons : Constraint + SOS1 constraint + var : Variable + variable to append """ PY_SCIP_CALL(SCIPappendVarSOS1(self._scip, cons.scip_cons, var.scip_var)) def addVarSOS2(self, Constraint cons, Variable var, weight): - """Add variable to SOS2 constraint. + """ + Add variable to SOS2 constraint. - :param Constraint cons: SOS2 constraint - :param Variable var: new variable - :param weight: weight of new variable + Parameters + ---------- + cons : Constraint + SOS2 constraint + var : Variable + new variable + weight : weight + weight of new variable """ PY_SCIP_CALL(SCIPaddVarSOS2(self._scip, cons.scip_cons, var.scip_var, weight)) def appendVarSOS2(self, Constraint cons, Variable var): - """Append variable to SOS2 constraint. + """ + Append variable to SOS2 constraint. - :param Constraint cons: SOS2 constraint - :param Variable var: variable to append + Parameters + ---------- + cons : Constraint + SOS2 constraint + var : Variable + variable to append """ PY_SCIP_CALL(SCIPappendVarSOS2(self._scip, cons.scip_cons, var.scip_var)) def setInitial(self, Constraint cons, newInit): - """Set "initial" flag of a constraint. + """ + Set "initial" flag of a constraint. + + Parameters + ---------- + cons : Constraint + newInit : bool - Keyword arguments: - cons -- constraint - newInit -- new initial value """ PY_SCIP_CALL(SCIPsetConsInitial(self._scip, cons.scip_cons, newInit)) def setRemovable(self, Constraint cons, newRem): - """Set "removable" flag of a constraint. + """ + Set "removable" flag of a constraint. + + Parameters + ---------- + cons : Constraint + newRem : bool - Keyword arguments: - cons -- constraint - newRem -- new removable value """ PY_SCIP_CALL(SCIPsetConsRemovable(self._scip, cons.scip_cons, newRem)) def setEnforced(self, Constraint cons, newEnf): - """Set "enforced" flag of a constraint. + """ + Set "enforced" flag of a constraint. + + Parameters + ---------- + cons : Constraint + newEnf : bool - Keyword arguments: - cons -- constraint - newEnf -- new enforced value """ PY_SCIP_CALL(SCIPsetConsEnforced(self._scip, cons.scip_cons, newEnf)) def setCheck(self, Constraint cons, newCheck): - """Set "check" flag of a constraint. + """ + Set "check" flag of a constraint. + + Parameters + ---------- + cons : Constraint + newCheck : bool - Keyword arguments: - cons -- constraint - newCheck -- new check value """ PY_SCIP_CALL(SCIPsetConsChecked(self._scip, cons.scip_cons, newCheck)) def chgRhs(self, Constraint cons, rhs): - """Change right hand side value of a constraint. + """ + Change right-hand side value of a constraint. - :param Constraint cons: linear or quadratic constraint - :param rhs: new right hand side (set to None for +infinity) + Parameters + ---------- + cons : Constraint + linear or quadratic constraint + rhs : float or None + new right-hand side (set to None for +infinity) """ @@ -3217,10 +5492,15 @@ cdef class Model: raise Warning("method cannot be called for constraints of type " + constype) def chgLhs(self, Constraint cons, lhs): - """Change left hand side value of a constraint. + """ + Change left-hand side value of a constraint. - :param Constraint cons: linear or quadratic constraint - :param lhs: new left hand side (set to None for -infinity) + Parameters + ---------- + cons : Constraint + linear or quadratic constraint + lhs : float or None + new left-hand side (set to None for -infinity) """ @@ -3236,9 +5516,17 @@ cdef class Model: raise Warning("method cannot be called for constraints of type " + constype) def getRhs(self, Constraint cons): - """Retrieve right hand side value of a constraint. + """ + Retrieve right-hand side value of a constraint. + + Parameters + ---------- + cons : Constraint + linear or quadratic constraint - :param Constraint cons: linear or quadratic constraint + Returns + ------- + float """ constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8') @@ -3250,9 +5538,17 @@ cdef class Model: raise Warning("method cannot be called for constraints of type " + constype) def getLhs(self, Constraint cons): - """Retrieve left hand side value of a constraint. + """ + Retrieve left-hand side value of a constraint. + + Parameters + ---------- + cons : Constraint + linear or quadratic constraint - :param Constraint cons: linear or quadratic constraint + Returns + ------- + float """ constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8') @@ -3264,47 +5560,73 @@ cdef class Model: raise Warning("method cannot be called for constraints of type " + constype) def chgCoefLinear(self, Constraint cons, Variable var, value): - """Changes coefficient of variable in linear constraint; + """ + Changes coefficient of variable in linear constraint; deletes the variable if coefficient is zero; adds variable if not yet contained in the constraint This method may only be called during problem creation stage for an original constraint and variable. This method requires linear time to search for occurences of the variable in the constraint data. - :param Constraint cons: linear constraint - :param Variable var: variable of constraint entry - :param value: new coefficient of constraint entry + Parameters + ---------- + cons : Constraint + linear constraint + var : Variable + variable of constraint entry + value : float + new coefficient of constraint entry """ PY_SCIP_CALL( SCIPchgCoefLinear(self._scip, cons.scip_cons, var.scip_var, value) ) def delCoefLinear(self, Constraint cons, Variable var): - """Deletes variable from linear constraint + """ + Deletes variable from linear constraint This method may only be called during problem creation stage for an original constraint and variable. - This method requires linear time to search for occurences of the variable in the constraint data. + This method requires linear time to search for occurrences of the variable in the constraint data. - :param Constraint cons: linear constraint - :param Variable var: variable of constraint entry + Parameters + ---------- + cons : Constraint + linear constraint + var : Variable + variable of constraint entry """ PY_SCIP_CALL( SCIPdelCoefLinear(self._scip, cons.scip_cons, var.scip_var) ) def addCoefLinear(self, Constraint cons, Variable var, value): - """Adds coefficient to linear constraint (if it is not zero) + """ + Adds coefficient to linear constraint (if it is not zero) - :param Constraint cons: linear constraint - :param Variable var: variable of constraint entry - :param value: coefficient of constraint entry + Parameters + ---------- + cons : Constraint + linear constraint + var : Variable + variable of constraint entry + value : float + coefficient of constraint entry """ PY_SCIP_CALL( SCIPaddCoefLinear(self._scip, cons.scip_cons, var.scip_var, value) ) def getActivity(self, Constraint cons, Solution sol = None): - """Retrieve activity of given constraint. + """ + Retrieve activity of given constraint. Can only be called after solving is completed. - :param Constraint cons: linear or quadratic constraint - :param Solution sol: solution to compute activity of, None to use current node's solution (Default value = None) + Parameters + ---------- + cons : Constraint + linear or quadratic constraint + sol : Solution or None, optional + solution to compute activity of, None to use current node's solution (Default value = None) + + Returns + ------- + float """ cdef SCIP_Real activity @@ -3328,13 +5650,22 @@ cdef class Model: def getSlack(self, Constraint cons, Solution sol = None, side = None): - """Retrieve slack of given constraint. + """ + Retrieve slack of given constraint. Can only be called after solving is completed. + Parameters + ---------- + cons : Constraint + linear or quadratic constraint + sol : Solution or None, optional + solution to compute slack of, None to use current node's solution (Default value = None) + side : str or None, optional + whether to use 'lhs' or 'rhs' for ranged constraints, None to return minimum (Default value = None) - :param Constraint cons: linear or quadratic constraint - :param Solution sol: solution to compute slack of, None to use current node's solution (Default value = None) - :param side: whether to use 'lhs' or 'rhs' for ranged constraints, None to return minimum (Default value = None) + Returns + ------- + float """ cdef SCIP_Real activity @@ -3368,9 +5699,16 @@ cdef class Model: return min(lhsslack, rhsslack) def getTransformedCons(self, Constraint cons): - """Retrieve transformed constraint. + """ + Retrieve transformed constraint. + + Parameters + ---------- + cons : Constraint - :param Constraint cons: constraint + Returns + ------- + Constraint """ cdef SCIP_CONS* transcons @@ -3378,25 +5716,55 @@ cdef class Model: return Constraint.create(transcons) def isNLPConstructed(self): - """returns whether SCIP's internal NLP has been constructed""" + """ + Returns whether SCIP's internal NLP has been constructed. + + Returns + ------- + bool + + """ return SCIPisNLPConstructed(self._scip) def getNNlRows(self): - """gets current number of nonlinear rows in SCIP's internal NLP""" + """ + Gets current number of nonlinear rows in SCIP's internal NLP. + + Returns + ------- + int + + """ return SCIPgetNNLPNlRows(self._scip) def getNlRows(self): - """returns a list with the nonlinear rows in SCIP's internal NLP""" + """ + Returns a list with the nonlinear rows in SCIP's internal NLP. + + Returns + ------- + list of NLRow + + """ cdef SCIP_NLROW** nlrows nlrows = SCIPgetNLPNlRows(self._scip) return [NLRow.create(nlrows[i]) for i in range(self.getNNlRows())] def getNlRowSolActivity(self, NLRow nlrow, Solution sol = None): - """gives the activity of a nonlinear row for a given primal solution - Keyword arguments: - nlrow -- nonlinear row - solution -- a primal solution, if None, then the current LP solution is used + """ + Gives the activity of a nonlinear row for a given primal solution. + + Parameters + ---------- + nlrow : NLRow + sol : Solution or None, optional + a primal solution, if None, then the current LP solution is used + + Returns + ------- + float + """ cdef SCIP_Real activity cdef SCIP_SOL* solptr @@ -3406,10 +5774,19 @@ cdef class Model: return activity def getNlRowSolFeasibility(self, NLRow nlrow, Solution sol = None): - """gives the feasibility of a nonlinear row for a given primal solution - Keyword arguments: - nlrow -- nonlinear row - solution -- a primal solution, if None, then the current LP solution is used + """ + Gives the feasibility of a nonlinear row for a given primal solution + + Parameters + ---------- + nlrow : NLRow + sol : Solution or None, optional + a primal solution, if None, then the current LP solution is used + + Returns + ------- + bool + """ cdef SCIP_Real feasibility cdef SCIP_SOL* solptr @@ -3419,7 +5796,18 @@ cdef class Model: return feasibility def getNlRowActivityBounds(self, NLRow nlrow): - """gives the minimal and maximal activity of a nonlinear row w.r.t. the variable's bounds""" + """ + Gives the minimal and maximal activity of a nonlinear row w.r.t. the variable's bounds. + + Parameters + ---------- + nlrow : NLRow + + Returns + ------- + tuple of float + + """ cdef SCIP_Real minactivity cdef SCIP_Real maxactivity @@ -3427,13 +5815,27 @@ cdef class Model: return (minactivity, maxactivity) def printNlRow(self, NLRow nlrow): - """prints nonlinear row""" + """ + Prints nonlinear row. + + Parameters + ---------- + nlrow : NLRow + + """ PY_SCIP_CALL( SCIPprintNlRow(self._scip, nlrow.scip_nlrow, NULL) ) def checkQuadraticNonlinear(self, Constraint cons): - """returns if the given constraint is quadratic + """ + Returns if the given constraint is quadratic. + + Parameters + ---------- + cons : Constraint - :param Constraint cons: constraint + Returns + ------- + bool """ cdef SCIP_Bool isquadratic @@ -3441,9 +5843,18 @@ cdef class Model: return isquadratic def getTermsQuadratic(self, Constraint cons): - """Retrieve bilinear, quadratic, and linear terms of a quadratic constraint. + """ + Retrieve bilinear, quadratic, and linear terms of a quadratic constraint. + + Parameters + ---------- + cons : Constraint - :param Constraint cons: constraint + Returns + ------- + bilinterms : list of tuple + quadterms : list of tuple + linterms : list of tuple """ cdef SCIP_EXPR* expr @@ -3504,13 +5915,30 @@ cdef class Model: return (bilinterms, quadterms, linterms) def setRelaxSolVal(self, Variable var, val): - """sets the value of the given variable in the global relaxation solution""" + """ + Sets the value of the given variable in the global relaxation solution. + + Parameters + ---------- + var : Variable + val : float + + """ PY_SCIP_CALL(SCIPsetRelaxSolVal(self._scip, NULL, var.scip_var, val)) def getConss(self, transformed=True): - """Retrieve all constraints. - - :param transformed: get transformed variables instead of original (Default value = True) + """ + Retrieve all constraints. + + Parameters + ---------- + transformed : bool, optional + get transformed variables instead of original (Default value = True) + + Returns + ------- + list of Constraint + """ cdef SCIP_CONS** _conss cdef int _nconss @@ -3525,32 +5953,60 @@ cdef class Model: return [Constraint.create(_conss[i]) for i in range(_nconss)] def getNConss(self, transformed=True): - """Retrieve number of all constraints""" + """ + Retrieve number of all constraints. + + Parameters + ---------- + transformed : bool, optional + get number of transformed variables instead of original (Default value = True) + + Returns + ------- + int + + """ if transformed: return SCIPgetNConss(self._scip) else: return SCIPgetNOrigConss(self._scip) def delCons(self, Constraint cons): - """Delete constraint from the model + """ + Delete constraint from the model - :param Constraint cons: constraint to be deleted + Parameters + ---------- + cons : Constraint + constraint to be deleted """ PY_SCIP_CALL(SCIPdelCons(self._scip, cons.scip_cons)) def delConsLocal(self, Constraint cons): - """Delete constraint from the current node and it's children + """ + Delete constraint from the current node and its children. - :param Constraint cons: constraint to be deleted + Parameters + ---------- + cons : Constraint + constraint to be deleted """ PY_SCIP_CALL(SCIPdelConsLocal(self._scip, cons.scip_cons)) def getValsLinear(self, Constraint cons): - """Retrieve the coefficients of a linear constraint + """ + Retrieve the coefficients of a linear constraint + + Parameters + ---------- + cons : Constraint + linear constraint to get the coefficients of - :param Constraint cons: linear constraint to get the coefficients of + Returns + ------- + dict of str to float """ cdef SCIP_Real* _vals @@ -3569,10 +6025,18 @@ cdef class Model: return valsdict def getRowLinear(self, Constraint cons): - """Retrieve the linear relaxation of the given linear constraint as a row. - may return NULL if no LP row was yet created; the user must not modify the row! + """ + Retrieve the linear relaxation of the given linear constraint as a row. + may return NULL if no LP row was yet created; the user must not modify the row! + + Parameters + ---------- + cons : Constraint + linear constraint to get the coefficients of - :param Constraint cons: linear constraint to get the coefficients of + Returns + ------- + Row """ constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8') @@ -3583,9 +6047,17 @@ cdef class Model: return Row.create(row) def getDualsolLinear(self, Constraint cons): - """Retrieve the dual solution to a linear constraint. + """ + Retrieve the dual solution to a linear constraint. + + Parameters + ---------- + cons : Constraint + linear constraint - :param Constraint cons: linear constraint + Returns + ------- + float """ constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8') @@ -3598,18 +6070,34 @@ cdef class Model: return SCIPgetDualsolLinear(self._scip, transcons.scip_cons) def getDualMultiplier(self, Constraint cons): - """DEPRECATED: Retrieve the dual solution to a linear constraint. + """ + DEPRECATED: Retrieve the dual solution to a linear constraint. + + Parameters + ---------- + cons : Constraint + linear constraint - :param Constraint cons: linear constraint + Returns + ------- + float """ raise Warning("model.getDualMultiplier(cons) is deprecated: please use model.getDualsolLinear(cons)") return self.getDualsolLinear(cons) def getDualfarkasLinear(self, Constraint cons): - """Retrieve the dual farkas value to a linear constraint. + """ + Retrieve the dual farkas value to a linear constraint. + + Parameters + ---------- + cons : Constraint + linear constraint - :param Constraint cons: linear constraint + Returns + ------- + float """ # TODO this should ideally be handled on the SCIP side @@ -3620,9 +6108,17 @@ cdef class Model: return SCIPgetDualfarkasLinear(self._scip, cons.scip_cons) def getVarRedcost(self, Variable var): - """Retrieve the reduced cost of a variable. + """ + Retrieve the reduced cost of a variable. + + Parameters + ---------- + var : Variable + variable to get the reduced cost of - :param Variable var: variable to get the reduced cost of + Returns + ------- + float """ redcost = None @@ -3635,10 +6131,20 @@ cdef class Model: return redcost def getDualSolVal(self, Constraint cons, boundconstraint=False): - """Retrieve returns dual solution value of a constraint. + """ + Returns dual solution value of a constraint. + + Parameters + ---------- + cons : Constraint + constraint to get the dual solution value of + boundconstraint : bool, optional + Decides whether to store a bool if the constraint is a bound constraint + (default = False) - :param Constraint cons: constraint to get the dual solution value of - :param boundconstraint bool: Decides whether to store a bool if the constraint is a bound constraint + Returns + ------- + float """ cdef SCIP_Real _dualsol @@ -3673,10 +6179,14 @@ cdef class Model: # Benders' decomposition methods def initBendersDefault(self, subproblems): - """initialises the default Benders' decomposition with a dictionary of subproblems + """ + Initialises the default Benders' decomposition with a dictionary of subproblems. + + Parameters + ---------- + subproblems : Model or dict of object to Model + a single Model instance or dictionary of Model instances - Keyword arguments: - subproblems -- a single Model instance or dictionary of Model instances """ cdef SCIP** subprobs cdef SCIP_BENDERS* benders @@ -3712,7 +6222,6 @@ cdef class Model: """Solves the subproblems with the best solution to the master problem. Afterwards, the best solution from each subproblem can be queried to get the solution to the original problem. - If the user wants to resolve the subproblems, they must free them by calling freeBendersSubproblems() """ @@ -3737,8 +6246,7 @@ cdef class Model: def freeBendersSubproblems(self): """Calls the free subproblem function for the Benders' decomposition. - This will free all subproblems for all decompositions. - """ + This will free all subproblems for all decompositions. """ cdef SCIP_BENDERS** _benders cdef int nbenders cdef int nsubproblems @@ -3754,9 +6262,16 @@ cdef class Model: j)) def updateBendersLowerbounds(self, lowerbounds, Benders benders=None): - """"updates the subproblem lower bounds for benders using + """ + Updates the subproblem lower bounds for benders using the lowerbounds dict. If benders is None, then the default - Benders' decomposition is updated + Benders' decomposition is updated. + + Parameters + ---------- + lowerbounds : dict of int to float + benders : Benders or None, optional + """ cdef SCIP_BENDERS* _benders @@ -3771,44 +6286,66 @@ cdef class Model: SCIPbendersUpdateSubproblemLowerbound(_benders, d, lowerbounds[d]) def activateBenders(self, Benders benders, int nsubproblems): - """Activates the Benders' decomposition plugin with the input name + """ + Activates the Benders' decomposition plugin with the input name. + + Parameters + ---------- + benders : Benders + the Benders' decomposition to which the subproblem belongs to + nsubproblems : int + the number of subproblems in the Benders' decomposition - Keyword arguments: - benders -- the Benders' decomposition to which the subproblem belongs to - nsubproblems -- the number of subproblems in the Benders' decomposition """ PY_SCIP_CALL(SCIPactivateBenders(self._scip, benders._benders, nsubproblems)) def addBendersSubproblem(self, Benders benders, subproblem): - """adds a subproblem to the Benders' decomposition given by the input + """ + Adds a subproblem to the Benders' decomposition given by the input name. - Keyword arguments: - benders -- the Benders' decomposition to which the subproblem belongs to - subproblem -- the subproblem to add to the decomposition - isconvex -- can be used to specify whether the subproblem is convex + Parameters + ---------- + benders : Benders + the Benders' decomposition to which the subproblem belongs to + subproblem : Model + the subproblem to add to the decomposition + """ PY_SCIP_CALL(SCIPaddBendersSubproblem(self._scip, benders._benders, (subproblem)._scip)) def setBendersSubproblemIsConvex(self, Benders benders, probnumber, isconvex = True): - """sets a flag indicating whether the subproblem is convex + """ + Sets a flag indicating whether the subproblem is convex. + + Parameters + ---------- + benders : Benders + the Benders' decomposition which contains the subproblem + probnumber : int + the problem number of the subproblem that the convexity will be set for + isconvex : bool, optional + flag to indicate whether the subproblem is convex (default=True) - Keyword arguments: - benders -- the Benders' decomposition which contains the subproblem - probnumber -- the problem number of the subproblem that the convexity will be set for - isconvex -- flag to indicate whether the subproblem is convex """ SCIPbendersSetSubproblemIsConvex(benders._benders, probnumber, isconvex) def setupBendersSubproblem(self, probnumber, Benders benders = None, Solution solution = None, checktype = PY_SCIP_BENDERSENFOTYPE.LP): - """ sets up the Benders' subproblem given the master problem solution + """ + Sets up the Benders' subproblem given the master problem solution. + + Parameters + ---------- + probnumber : int + the index of the problem that is to be set up + benders : Benders or None, optional + the Benders' decomposition to which the subproblem belongs to + solution : Solution or None, optional + the master problem solution that is used for the set up, if None, then the LP solution is used + checktype : PY_SCIP_BENDERSENFOTYPE + the type of solution check that prompted the solving of the Benders' subproblems, either + PY_SCIP_BENDERSENFOTYPE: LP, RELAX, PSEUDO or CHECK. Default is LP. - Keyword arguments: - probnumber -- the index of the problem that is to be set up - benders -- the Benders' decomposition to which the subproblem belongs to - solution -- the master problem solution that is used for the set up, if None, then the LP solution is used - checktype -- the type of solution check that prompted the solving of the Benders' subproblems, either - PY_SCIP_BENDERSENFOTYPE: LP, RELAX, PSEUDO or CHECK. Default is LP """ cdef SCIP_BENDERS* scip_benders cdef SCIP_SOL* scip_sol @@ -3828,14 +6365,28 @@ cdef class Model: PY_SCIP_CALL(retcode) def solveBendersSubproblem(self, probnumber, solvecip, Benders benders = None, Solution solution = None): - """ solves the Benders' decomposition subproblem. The convex relaxation will be solved unless + """ + Solves the Benders' decomposition subproblem. The convex relaxation will be solved unless the parameter solvecip is set to True. - Keyword arguments: - probnumber -- the index of the problem that is to be set up - solvecip -- should the CIP of the subproblem be solved, if False, then only the convex relaxation is solved - benders -- the Benders' decomposition to which the subproblem belongs to - solution -- the master problem solution that is used for the set up, if None, then the LP solution is used + Parameters + ---------- + probnumber : int + the index of the problem that is to be set up + solvecip : bool + whether the CIP of the subproblem should be solved. If False, then only the convex relaxation is solved. + benders : Benders or None, optional + the Benders' decomposition to which the subproblem belongs to + solution : Solution or None, optional + the master problem solution that is used for the set up, if None, then the LP solution is used + + Returns + ------- + infeasible : bool + returns whether the current subproblem is infeasible + objective : float or None + the objective function value of the subproblem, can be None + """ cdef SCIP_BENDERS* scip_benders @@ -3859,12 +6410,22 @@ cdef class Model: return infeasible, objective def getBendersSubproblem(self, probnumber, Benders benders = None): - """Returns a Model object that wraps around the SCIP instance of the subproblem. - NOTE: This Model object is just a place holder and SCIP instance will not be freed when the object is destroyed. + """ + Returns a Model object that wraps around the SCIP instance of the subproblem. + NOTE: This Model object is just a place holder and SCIP instance will not be + freed when the object is destroyed. + + Parameters + ---------- + probnumber : int + the problem number for subproblem that is required + benders : Benders or None, optional + the Benders' decomposition object that the subproblem belongs to (Default = None) + + Returns + ------- + Model - Keyword arguments: - probnumber -- the problem number for subproblem that is required - benders -- the Benders' decomposition object for the that the subproblem belongs to (Default = None) """ cdef SCIP_BENDERS* scip_benders cdef SCIP* scip_subprob @@ -3879,13 +6440,23 @@ cdef class Model: return Model.create(scip_subprob) def getBendersVar(self, Variable var, Benders benders = None, probnumber = -1): - """Returns the variable for the subproblem or master problem - depending on the input probnumber + """ + Returns the variable for the subproblem or master problem + depending on the input probnumber. + + Parameters + ---------- + var : Variable + the source variable for which the target variable is requested + benders : Benders or None, optional + the Benders' decomposition to which the subproblem variables belong to + probnumber : int, optional + the problem number for which the target variable belongs, -1 for master problem + + Returns + ------- + Variable or None - Keyword arguments: - var -- the source variable for which the target variable is requested - benders -- the Benders' decomposition to which the subproblem variables belong to - probnumber -- the problem number for which the target variable belongs, -1 for master problem """ cdef SCIP_BENDERS* _benders cdef SCIP_VAR* _mappedvar @@ -3908,11 +6479,20 @@ cdef class Model: return mappedvar def getBendersAuxiliaryVar(self, probnumber, Benders benders = None): - """Returns the auxiliary variable that is associated with the input problem number + """ + Returns the auxiliary variable that is associated with the input problem number + + Parameters + ---------- + probnumber : int + the problem number for which the target variable belongs, -1 for master problem + benders : Benders or None, optional + the Benders' decomposition to which the subproblem variables belong to + + Returns + ------- + Variable - Keyword arguments: - probnumber -- the problem number for which the target variable belongs, -1 for master problem - benders -- the Benders' decomposition to which the subproblem variables belong to """ cdef SCIP_BENDERS* _benders cdef SCIP_VAR* _auxvar @@ -3928,12 +6508,23 @@ cdef class Model: return auxvar def checkBendersSubproblemOptimality(self, Solution solution, probnumber, Benders benders = None): - """Returns whether the subproblem is optimal w.r.t the master problem auxiliary variables. + """ + Returns whether the subproblem is optimal w.r.t the master problem auxiliary variables. + + Parameters + ---------- + solution : Solution + the master problem solution that is being checked for optimamlity + probnumber : int + the problem number for which optimality is being checked + benders : Benders or None, optional + the Benders' decomposition to which the subproblem belongs to + + Returns + ------- + optimal : bool + flag to indicate whether the current subproblem is optimal for the master - Keyword arguments: - solution -- the master problem solution that is being checked for optimamlity - probnumber -- the problem number for which optimality is being checked - benders -- the Benders' decomposition to which the subproblem belongs to """ cdef SCIP_BENDERS* _benders cdef SCIP_SOL* scip_sol @@ -3955,21 +6546,31 @@ cdef class Model: return optimal def includeBendersDefaultCuts(self, Benders benders): - """includes the default Benders' decomposition cuts to the custom Benders' decomposition plugin + """ + Includes the default Benders' decomposition cuts to the custom Benders' decomposition plugin. + + Parameters + ---------- + benders : Benders + the Benders' decomposition that the default cuts will be applied to - Keyword arguments: - benders -- the Benders' decomposition that the default cuts will be applied to """ PY_SCIP_CALL( SCIPincludeBendersDefaultCuts(self._scip, benders._benders) ) def includeEventhdlr(self, Eventhdlr eventhdlr, name, desc): - """Include an event handler. + """ + Include an event handler. + + Parameters + ---------- + eventhdlr : Eventhdlr + event handler + name : str + name of event handler + desc : str + description of event handler - Keyword arguments: - eventhdlr -- event handler - name -- name of event handler - desc -- description of event handler """ n = str_conversion(name) d = str_conversion(desc) @@ -3988,13 +6589,22 @@ cdef class Model: Py_INCREF(eventhdlr) def includePricer(self, Pricer pricer, name, desc, priority=1, delay=True): - """Include a pricer. + """ + Include a pricer. - :param Pricer pricer: pricer - :param name: name of pricer - :param desc: description of pricer - :param priority: priority of pricer (Default value = 1) - :param delay: should the pricer be delayed until no other pricers or already existing problem variables with negative reduced costs are found? (Default value = True) + Parameters + ---------- + pricer : Pricer + pricer + name : str + name of pricer + desc : str + description of pricer + priority : int, optional + priority of pricer (Default value = 1) + delay : bool, optional + should the pricer be delayed until no other pricers or already existing problem variables + with negative reduced costs are found? (Default value = True) """ n = str_conversion(name) @@ -4015,23 +6625,44 @@ cdef class Model: delayprop=False, needscons=True, proptiming=PY_SCIP_PROPTIMING.BEFORELP, presoltiming=PY_SCIP_PRESOLTIMING.MEDIUM): - """Include a constraint handler - - :param Conshdlr conshdlr: constraint handler - :param name: name of constraint handler - :param desc: description of constraint handler - :param sepapriority: priority for separation (Default value = 0) - :param enfopriority: priority for constraint enforcing (Default value = 0) - :param chckpriority: priority for checking feasibility (Default value = 0) - :param sepafreq: frequency for separating cuts; 0 = only at root node (Default value = -1) - :param propfreq: frequency for propagating domains; 0 = only preprocessing propagation (Default value = -1) - :param eagerfreq: frequency for using all instead of only the useful constraints in separation, propagation and enforcement; -1 = no eager evaluations, 0 = first only (Default value = 100) - :param maxprerounds: maximal number of presolving rounds the constraint handler participates in (Default value = -1) - :param delaysepa: should separation method be delayed, if other separators found cuts? (Default value = False) - :param delayprop: should propagation method be delayed, if other propagators found reductions? (Default value = False) - :param needscons: should the constraint handler be skipped, if no constraints are available? (Default value = True) - :param proptiming: positions in the node solving loop where propagation method of constraint handlers should be executed (Default value = SCIP_PROPTIMING.BEFORELP) - :param presoltiming: timing mask of the constraint handler's presolving method (Default value = SCIP_PRESOLTIMING.MEDIUM) + """ + Include a constraint handler. + + Parameters + ---------- + conshdlr : Conshdlr + constraint handler + name : str + name of constraint handler + desc : str + description of constraint handler + sepapriority : int, optional + priority for separation (Default value = 0) + enfopriority : int, optional + priority for constraint enforcing (Default value = 0) + chckpriority : int, optional + priority for checking feasibility (Default value = 0) + sepafreq : int, optional + frequency for separating cuts; 0 = only at root node (Default value = -1) + propfreq : int, optional + frequency for propagating domains; 0 = only preprocessing propagation (Default value = -1) + eagerfreq : int, optional + frequency for using all instead of only the useful constraints in separation, + propagation and enforcement; -1 = no eager evaluations, 0 = first only + (Default value = 100) + maxprerounds : int, optional + maximal number of presolving rounds the constraint handler participates in (Default value = -1) + delaysepa : bool, optional + should separation method be delayed, if other separators found cuts? (Default value = False) + delayprop : bool, optional + should propagation method be delayed, if other propagators found reductions? (Default value = False) + needscons : bool, optional + should the constraint handler be skipped, if no constraints are available? (Default value = True) + proptiming : PY_SCIP_PROPTIMING + positions in the node solving loop where propagation method of constraint handlers + should be executed (Default value = SCIP_PROPTIMING.BEFORELP) + presoltiming : PY_SCIP_PRESOLTIMING + timing mask of the constraint handler's presolving method (Default value = SCIP_PRESOLTIMING.MEDIUM) """ n = str_conversion(name) @@ -4050,20 +6681,39 @@ cdef class Model: def createCons(self, Conshdlr conshdlr, name, initial=True, separate=True, enforce=True, check=True, propagate=True, local=False, modifiable=False, dynamic=False, removable=False, stickingatnode=False): - """Create a constraint of a custom constraint handler + """ + Create a constraint of a custom constraint handler. - :param Conshdlr conshdlr: constraint handler - :param name: name of constraint - :param initial: (Default value = True) - :param separate: (Default value = True) - :param enforce: (Default value = True) - :param check: (Default value = True) - :param propagate: (Default value = True) - :param local: (Default value = False) - :param modifiable: (Default value = False) - :param dynamic: (Default value = False) - :param removable: (Default value = False) - :param stickingatnode: (Default value = False) + Parameters + ---------- + conshdlr : Conshdlr + constraint handler + name : str + name of constraint handler + initial : bool, optional + (Default value = True) + separate : bool, optional + (Default value = True) + enforce : bool, optional + (Default value = True) + check : bool, optional + (Default value = True) + propagate : bool, optional + (Default value = True) + local : bool, optional + (Default value = False) + modifiable : bool, optional + (Default value = False) + dynamic : bool, optional + (Default value = False) + removable : bool, optional + (Default value = False) + stickingatnode : bool, optional + (Default value = False) + + Returns + ------- + Constraint """ @@ -4076,14 +6726,23 @@ cdef class Model: return constraint def includePresol(self, Presol presol, name, desc, priority, maxrounds, timing=SCIP_PRESOLTIMING_FAST): - """Include a presolver + """ + Include a presolver. - :param Presol presol: presolver - :param name: name of presolver - :param desc: description of presolver - :param priority: priority of the presolver (>= 0: before, < 0: after constraint handlers) - :param maxrounds: maximal number of presolving rounds the presolver participates in (-1: no limit) - :param timing: timing mask of presolver (Default value = SCIP_PRESOLTIMING_FAST) + Parameters + ---------- + presol : Presol + presolver + name : str + name of presolver + desc : str + description of presolver + priority : int + priority of the presolver (>= 0: before, < 0: after constraint handlers) + maxrounds : int + maximal number of presolving rounds the presolver participates in (-1: no limit) + timing : PY_SCIP_PRESOLTIMING, optional + timing mask of presolver (Default value = SCIP_PRESOLTIMING_FAST) """ n = str_conversion(name) @@ -4094,7 +6753,8 @@ cdef class Model: Py_INCREF(presol) def includeSepa(self, Sepa sepa, name, desc, priority=0, freq=10, maxbounddist=1.0, usessubscip=False, delay=False): - """Include a separator + """ + Include a separator :param Sepa sepa: separator :param name: name of separator @@ -4105,6 +6765,28 @@ cdef class Model: :param usessubscip: does the separator use a secondary SCIP instance? (Default value = False) :param delay: should separator be delayed, if other separators found cuts? (Default value = False) + + Parameters + ---------- + sepa : Sepa + separator + name : str + name of separator + desc : str + description of separator + priority : int, optional + priority of separator (>= 0: before, < 0: after constraint handlers) (default=0) + freq : int, optional + frequency for calling separator (default=10) + maxbounddist : float, optional + maximal relative distance from current node's dual bound to primal + bound compared to best node's dual bound for applying separation. + (default = 1.0) + usessubscip : bool, optional + does the separator use a secondary SCIP instance? (Default value = False) + delay : bool, optional + should separator be delayed if other separators found cuts? (Default value = False) + """ n = str_conversion(name) d = str_conversion(desc) @@ -4115,12 +6797,19 @@ cdef class Model: Py_INCREF(sepa) def includeReader(self, Reader reader, name, desc, ext): - """Include a reader + """ + Include a reader. - :param Reader reader: reader - :param name: name of reader - :param desc: description of reader - :param ext: file extension of reader + Parameters + ---------- + reader : Reader + reader + name : str + name of reader + desc : str + description of reader + ext : str + file extension of reader """ n = str_conversion(name) @@ -4134,18 +6823,31 @@ cdef class Model: def includeProp(self, Prop prop, name, desc, presolpriority, presolmaxrounds, proptiming, presoltiming=SCIP_PRESOLTIMING_FAST, priority=1, freq=1, delay=True): - """Include a propagator. + """ + Include a propagator. - :param Prop prop: propagator - :param name: name of propagator - :param desc: description of propagator - :param presolpriority: presolving priority of the propgator (>= 0: before, < 0: after constraint handlers) - :param presolmaxrounds: maximal number of presolving rounds the propagator participates in (-1: no limit) - :param proptiming: positions in the node solving loop where propagation method of constraint handlers should be executed - :param presoltiming: timing mask of the constraint handler's presolving method (Default value = SCIP_PRESOLTIMING_FAST) - :param priority: priority of the propagator (Default value = 1) - :param freq: frequency for calling propagator (Default value = 1) - :param delay: should propagator be delayed if other propagators have found reductions? (Default value = True) + Parameters + ---------- + prop : Prop + propagator + name : str + name of propagator + desc : str + description of propagator + presolpriority : int + presolving priority of the propgator (>= 0: before, < 0: after constraint handlers) + presolmaxrounds : int + maximal number of presolving rounds the propagator participates in (-1: no limit) + proptiming : SCIP_PROPTIMING + positions in the node solving loop where propagation method of constraint handlers should be executed + presoltiming : PY_SCIP_PRESOLTIMING, optional + timing mask of the constraint handler's presolving method (Default value = SCIP_PRESOLTIMING_FAST) + priority : int, optional + priority of the propagator (Default value = 1) + freq : int, optional + frequency for calling propagator (Default value = 1) + delay : bool, optional + should propagator be delayed if other propagators have found reductions? (Default value = True) """ n = str_conversion(name) @@ -4162,18 +6864,32 @@ cdef class Model: def includeHeur(self, Heur heur, name, desc, dispchar, priority=10000, freq=1, freqofs=0, maxdepth=-1, timingmask=SCIP_HEURTIMING_BEFORENODE, usessubscip=False): - """Include a primal heuristic. + """ + Include a primal heuristic. - :param Heur heur: heuristic - :param name: name of heuristic - :param desc: description of heuristic - :param dispchar: display character of heuristic - :param priority: priority of the heuristic (Default value = 10000) - :param freq: frequency for calling heuristic (Default value = 1) - :param freqofs: frequency offset for calling heuristic (Default value = 0) - :param maxdepth: maximal depth level to call heuristic at (Default value = -1) - :param timingmask: positions in the node solving loop where heuristic should be executed (Default value = SCIP_HEURTIMING_BEFORENODE) - :param usessubscip: does the heuristic use a secondary SCIP instance? (Default value = False) + Parameters + ---------- + heur : Heur + heuristic + name : str + name of heuristic + desc : str + description of heuristic + dispchar : str + display character of heuristic. Please use a single length string. + priority : int. optional + priority of the heuristic (Default value = 10000) + freq : int, optional + frequency for calling heuristic (Default value = 1) + freqofs : int. optional + frequency offset for calling heuristic (Default value = 0) + maxdepth : int, optional + maximal depth level to call heuristic at (Default value = -1) + timingmask : PY_SCIP_HEURTIMING, optional + positions in the node solving loop where heuristic should be executed + (Default value = SCIP_HEURTIMING_BEFORENODE) + usessubscip : bool, optional + does the heuristic use a secondary SCIP instance? (Default value = False) """ nam = str_conversion(name) @@ -4190,13 +6906,21 @@ cdef class Model: Py_INCREF(heur) def includeRelax(self, Relax relax, name, desc, priority=10000, freq=1): - """Include a relaxation handler. + """ + Include a relaxation handler. - :param Relax relax: relaxation handler - :param name: name of relaxation handler - :param desc: description of relaxation handler - :param priority: priority of the relaxation handler (negative: after LP, non-negative: before LP, Default value = 10000) - :param freq: frequency for calling relaxation handler + Parameters + ---------- + relax : Relax + relaxation handler + name : str + name of relaxation handler + desc : str + description of relaxation handler + priority : int, optional + priority of the relaxation handler (negative: after LP, non-negative: before LP, Default value = 10000) + freq : int, optional + frequency for calling relaxation handler """ nam = str_conversion(name) @@ -4209,12 +6933,20 @@ cdef class Model: Py_INCREF(relax) def includeCutsel(self, Cutsel cutsel, name, desc, priority): - """include a cut selector + """ + Include a cut selector. + + Parameters + ---------- + cutsel : Cutsel + cut selector + name : str + name of cut selector + desc : str + description of cut selector + priority : int + priority of the cut selector - :param Cutsel cutsel: cut selector - :param name: name of cut selector - :param desc: description of cut selector - :param priority: priority of the cut selector """ nam = str_conversion(name) @@ -4227,14 +6959,25 @@ cdef class Model: Py_INCREF(cutsel) def includeBranchrule(self, Branchrule branchrule, name, desc, priority, maxdepth, maxbounddist): - """Include a branching rule. + """ + Include a branching rule. - :param Branchrule branchrule: branching rule - :param name: name of branching rule - :param desc: description of branching rule - :param priority: priority of branching rule - :param maxdepth: maximal depth level up to which this branching rule should be used (or -1) - :param maxbounddist: maximal relative distance from current node's dual bound to primal bound compared to best node's dual bound for applying branching rule (0.0: only on current best node, 1.0: on all nodes) + Parameters + ---------- + branchrule : Branchrule + branching rule + name : str + name of branching rule + desc : str + description of branching rule + priority : int + priority of branching rule + maxdepth : int + maximal depth level up to which this branching rule should be used (or -1) + maxbounddist : float + maximal relative distance from current node's dual bound to primal bound + compared to best node's dual bound for applying branching rule + (0.0: only on current best node, 1.0: on all nodes) """ nam = str_conversion(name) @@ -4248,13 +6991,21 @@ cdef class Model: Py_INCREF(branchrule) def includeNodesel(self, Nodesel nodesel, name, desc, stdpriority, memsavepriority): - """Include a node selector. + """ + Include a node selector. - :param Nodesel nodesel: node selector - :param name: name of node selector - :param desc: description of node selector - :param stdpriority: priority of the node selector in standard mode - :param memsavepriority: priority of the node selector in memory saving mode + Parameters + ---------- + nodesel : Nodesel + node selector + name : str + name of node selector + desc : str + description of node selector + stdpriority : int + priority of the node selector in standard mode + memsavepriority : int + priority of the node selector in memory saving mode """ nam = str_conversion(name) @@ -4269,17 +7020,29 @@ cdef class Model: def includeBenders(self, Benders benders, name, desc, priority=1, cutlp=True, cutpseudo=True, cutrelax=True, shareaux=False): - """Include a Benders' decomposition. - - Keyword arguments: - benders -- the Benders decomposition - name -- the name - desc -- the description - priority -- priority of the Benders' decomposition - cutlp -- should Benders' cuts be generated from LP solutions - cutpseudo -- should Benders' cuts be generated from pseudo solutions - cutrelax -- should Benders' cuts be generated from relaxation solutions - shareaux -- should the Benders' decomposition share the auxiliary variables of the highest priority Benders' decomposition + """ + Include a Benders' decomposition. + + Parameters + ---------- + benders : Benders + the Benders decomposition + name : str + the name + desc : str + the description + priority : int, optional + priority of the Benders' decomposition + cutlp : bool, optional + should Benders' cuts be generated from LP solutions + cutpseudo : bool, optional + should Benders' cuts be generated from pseudo solutions + cutrelax : bool, optional + should Benders' cuts be generated from relaxation solutions + shareaux : bool, optional + should the Benders' decomposition share the auxiliary variables of the + highest priority Benders' decomposition + """ n = str_conversion(name) d = str_conversion(desc) @@ -4298,15 +7061,25 @@ cdef class Model: Py_INCREF(benders) def includeBenderscut(self, Benders benders, Benderscut benderscut, name, desc, priority=1, islpcut=True): - """ Include a Benders' decomposition cutting method + """ + Include a Benders' decomposition cutting method + + Parameters + ---------- + benders : Benders + the Benders' decomposition that this cutting method is attached to + benderscut : Benderscut + the Benders' decomposition cutting method + name : str + the name + desc : str + the description + priority : int. optional + priority of the Benders' decomposition (Default = 1) + islpcut : bool, optional + is this cutting method suitable for generating cuts for convex relaxations? + (Default = True) - Keyword arguments: - benders -- the Benders' decomposition that this cutting method is attached to - benderscut --- the Benders' decomposition cutting method - name -- the name - desc -- the description - priority -- priority of the Benders' decomposition - islpcut -- is this cutting method suitable for generating cuts for convex relaxations? """ cdef SCIP_BENDERS* _benders @@ -4329,20 +7102,27 @@ cdef class Model: def getLPBranchCands(self): - """gets branching candidates for LP solution branching (fractional variables) along with solution values, + """ + Gets branching candidates for LP solution branching (fractional variables) along with solution values, fractionalities, and number of branching candidates; The number of branching candidates does NOT account for fractional implicit integer variables which should not be used for branching decisions. Fractional - implicit integer variables are stored at the positions *nlpcands to *nlpcands + *nfracimplvars - 1 + implicit integer variables are stored at the positions nlpcands to nlpcands + nfracimplvars - 1 branching rules should always select the branching candidate among the first npriolpcands of the candidate list - :return tuple (lpcands, lpcandssol, lpcadsfrac, nlpcands, npriolpcands, nfracimplvars) where - - lpcands: list of variables of LP branching candidates - lpcandssol: list of LP candidate solution values - lpcandsfrac list of LP candidate fractionalities - nlpcands: number of LP branching candidates - npriolpcands: number of candidates with maximal priority - nfracimplvars: number of fractional implicit integer variables + Returns + ------- + list of Variable + list of variables of LP branching candidates + list of float + list of LP candidate solution values + list of float + list of LP candidate fractionalities + int + number of LP branching candidates + int + number of candidates with maximal priority + int + number of fractional implicit integer variables """ cdef int ncands @@ -4361,14 +7141,18 @@ cdef class Model: [lpcandsfrac[i] for i in range(nlpcands)], nlpcands, npriolpcands, nfracimplvars) def getPseudoBranchCands(self): - """gets branching candidates for pseudo solution branching (non-fixed variables) + """ + Gets branching candidates for pseudo solution branching (non-fixed variables) along with the number of candidates. - :return tuple (pseudocands, npseudocands, npriopseudocands) where - - pseudocands: list of variables of pseudo branching candidates - npseudocands: number of pseudo branching candidates - npriopseudocands: number of candidates with maximal priority + Returns + ------- + list of Variable + list of variables of pseudo branching candidates + int + number of pseudo branching candidates + int + number of candidates with maximal priority """ cdef int npseudocands @@ -4381,11 +7165,22 @@ cdef class Model: return ([Variable.create(pseudocands[i]) for i in range(npseudocands)], npseudocands, npriopseudocands) def branchVar(self, Variable variable): - """Branch on a non-continuous variable. + """ + Branch on a non-continuous variable. + + Parameters + ---------- + variable : Variable + Variable to branch on - :param variable: Variable to branch on - :return: tuple(downchild, eqchild, upchild) of Nodes of the left, middle and right child. Middle child only exists - if branch variable is integer (it is None otherwise) + Returns + ------- + Node + Node created for the down (left) branch + Node or None + Node created for the equal child (middle child). Only exists if branch variable is integer + Node + Node created for the up (right) branch """ cdef SCIP_NODE* downchild @@ -4397,12 +7192,24 @@ cdef class Model: def branchVarVal(self, variable, value): - """Branches on variable using a value which separates the domain of the variable. + """ + Branches on variable using a value which separates the domain of the variable. + + Parameters + ---------- + variable : Variable + Variable to branch on + value : float + value to branch on - :param variable: Variable to branch on - :param value: float, value to branch on - :return: tuple(downchild, eqchild, upchild) of Nodes of the left, middle and right child. Middle child only exists - if branch variable is integer (it is None otherwise) + Returns + ------- + Node + Node created for the down (left) branch + Node or None + Node created for the equal child (middle child). Only exists if the branch variable is integer + Node + Node created for the up (right) branch """ cdef SCIP_NODE* downchild @@ -4414,36 +7221,64 @@ cdef class Model: return Node.create(downchild), Node.create(eqchild), Node.create(upchild) def calcNodeselPriority(self, Variable variable, branchdir, targetvalue): - """calculates the node selection priority for moving the given variable's LP value + """ + Calculates the node selection priority for moving the given variable's LP value to the given target value; - this node selection priority can be given to the SCIPcreateChild() call + this node selection priority can be given to the SCIPcreateChild() call. + + Parameters + ---------- + variable : Variable + variable on which the branching is applied + branchdir : PY_SCIP_BRANCHDIR + type of branching that was performed + targetvalue : float + new value of the variable in the child node - :param variable: variable on which the branching is applied - :param branchdir: type of branching that was performed - :param targetvalue: new value of the variable in the child node - :return: node selection priority for moving the given variable's LP value to the given target value + Returns + ------- + int + node selection priority for moving the given variable's LP value to the given target value """ return SCIPcalcNodeselPriority(self._scip, variable.scip_var, branchdir, targetvalue) def calcChildEstimate(self, Variable variable, targetvalue): - """Calculates an estimate for the objective of the best feasible solution + """ + Calculates an estimate for the objective of the best feasible solution contained in the subtree after applying the given branching; - this estimate can be given to the SCIPcreateChild() call + this estimate can be given to the SCIPcreateChild() call. + + Parameters + ---------- + variable : Variable + Variable to compute the estimate for + targetvalue : float + new value of the variable in the child node - :param variable: Variable to compute the estimate for - :param targetvalue: new value of the variable in the child node - :return: objective estimate of the best solution in the subtree after applying the given branching + Returns + ------- + float + objective estimate of the best solution in the subtree after applying the given branching """ return SCIPcalcChildEstimate(self._scip, variable.scip_var, targetvalue) def createChild(self, nodeselprio, estimate): - """Create a child node of the focus node. + """ + Create a child node of the focus node. + + Parameters + ---------- + nodeselprio : int + node selection priority of new node + estimate : float + estimate for (transformed) objective value of best feasible solution in subtree - :param nodeselprio: float, node selection priority of new node - :param estimate: float, estimate for(transformed) objective value of best feasible solution in subtree - :return: Node, the child which was created + Returns + ------- + Node + the child which was created """ cdef SCIP_NODE* child @@ -4452,59 +7287,135 @@ cdef class Model: # Diving methods (Diving is LP related) def startDive(self): - """Initiates LP diving - It allows the user to change the LP in several ways, solve, change again, etc, without affecting the actual LP that has. When endDive() is called, - SCIP will undo all changes done and recover the LP it had before startDive - """ + """Initiates LP diving. + It allows the user to change the LP in several ways, solve, change again, etc, + without affecting the actual LP. When endDive() is called, + SCIP will undo all changes done and recover the LP it had before startDive.""" PY_SCIP_CALL(SCIPstartDive(self._scip)) def endDive(self): - """Quits probing and resets bounds and constraints to the focus node's environment""" + """Quits probing and resets bounds and constraints to the focus node's environment.""" PY_SCIP_CALL(SCIPendDive(self._scip)) def chgVarObjDive(self, Variable var, newobj): - """changes (column) variable's objective value in current dive""" + """ + Changes (column) variable's objective value in current dive. + + Parameters + ---------- + var : Variable + newobj : float + + """ PY_SCIP_CALL(SCIPchgVarObjDive(self._scip, var.scip_var, newobj)) def chgVarLbDive(self, Variable var, newbound): - """changes variable's current lb in current dive""" + """ + Changes variable's current lb in current dive. + + Parameters + ---------- + var : Variable + newbound : float + + """ PY_SCIP_CALL(SCIPchgVarLbDive(self._scip, var.scip_var, newbound)) def chgVarUbDive(self, Variable var, newbound): - """changes variable's current ub in current dive""" + """ + Changes variable's current ub in current dive. + + Parameters + ---------- + var : Variable + newbound : float + + """ PY_SCIP_CALL(SCIPchgVarUbDive(self._scip, var.scip_var, newbound)) def getVarLbDive(self, Variable var): - """returns variable's current lb in current dive""" + """ + Returns variable's current lb in current dive. + + Parameters + ---------- + var : Variable + + Returns + ------- + float + + """ return SCIPgetVarLbDive(self._scip, var.scip_var) def getVarUbDive(self, Variable var): - """returns variable's current ub in current dive""" + """ + Returns variable's current ub in current dive. + + Parameters + ---------- + var : Variable + + Returns + ------- + float + + """ return SCIPgetVarUbDive(self._scip, var.scip_var) def chgRowLhsDive(self, Row row, newlhs): - """changes row lhs in current dive, change will be undone after diving - ends, for permanent changes use SCIPchgRowLhs() + """ + Changes row lhs in current dive, change will be undone after diving + ends, for permanent changes use SCIPchgRowLhs(). + + Parameters + ---------- + row : Row + newlhs : float + """ PY_SCIP_CALL(SCIPchgRowLhsDive(self._scip, row.scip_row, newlhs)) def chgRowRhsDive(self, Row row, newrhs): - """changes row rhs in current dive, change will be undone after diving - ends, for permanent changes use SCIPchgRowLhs() + """ + Changes row rhs in current dive, change will be undone after diving + ends. For permanent changes use SCIPchgRowRhs(). + + Parameters + ---------- + row : Row + newrhs : float + """ PY_SCIP_CALL(SCIPchgRowRhsDive(self._scip, row.scip_row, newrhs)) def addRowDive(self, Row row): - """adds a row to the LP in current dive""" + """ + Adds a row to the LP in current dive. + + Parameters + ---------- + row : Row + + """ PY_SCIP_CALL(SCIPaddRowDive(self._scip, row.scip_row)) def solveDiveLP(self, itlim = -1): - """solves the LP of the current dive no separation or pricing is applied - no separation or pricing is applied - :param itlim: maximal number of LP iterations to perform (Default value = -1, that is, no limit) - returns two booleans: - lperror -- if an unresolved lp error occured - cutoff -- whether the LP was infeasible or the objective limit was reached + """ + Solves the LP of the current dive. No separation or pricing is applied. + + Parameters + ---------- + itlim : int, optional + maximal number of LP iterations to perform (Default value = -1, that is, no limit) + + Returns + ------- + lperror : bool + whether an unresolved lp error occured + cutoff : bool + whether the LP was infeasible or the objective limit was reached + """ cdef SCIP_Bool lperror cdef SCIP_Bool cutoff @@ -4513,79 +7424,137 @@ cdef class Model: return lperror, cutoff def inRepropagation(self): - """returns if the current node is already solved and only propagated again.""" + """ + Returns if the current node is already solved and only propagated again. + + Returns + ------- + bool + + """ return SCIPinRepropagation(self._scip) # Probing methods (Probing is tree based) def startProbing(self): """Initiates probing, making methods SCIPnewProbingNode(), SCIPbacktrackProbing(), SCIPchgVarLbProbing(), - SCIPchgVarUbProbing(), SCIPfixVarProbing(), SCIPpropagateProbing(), SCIPsolveProbingLP(), etc available + SCIPchgVarUbProbing(), SCIPfixVarProbing(), SCIPpropagateProbing(), SCIPsolveProbingLP(), etc available. """ PY_SCIP_CALL( SCIPstartProbing(self._scip) ) def endProbing(self): - """Quits probing and resets bounds and constraints to the focus node's environment""" + """Quits probing and resets bounds and constraints to the focus node's environment.""" PY_SCIP_CALL( SCIPendProbing(self._scip) ) def newProbingNode(self): - """creates a new probing sub node, whose changes can be undone by backtracking to a higher node in the - probing path with a call to backtrackProbing() + """Creates a new probing sub node, whose changes can be undone by backtracking to a higher node in the + probing path with a call to backtrackProbing(). """ PY_SCIP_CALL( SCIPnewProbingNode(self._scip) ) def backtrackProbing(self, probingdepth): - """undoes all changes to the problem applied in probing up to the given probing depth - :param probingdepth: probing depth of the node in the probing path that should be reactivated + """ + Undoes all changes to the problem applied in probing up to the given probing depth. + + Parameters + ---------- + probingdepth : int + probing depth of the node in the probing path that should be reactivated + """ PY_SCIP_CALL( SCIPbacktrackProbing(self._scip, probingdepth) ) def getProbingDepth(self): - """returns the current probing depth""" + """Returns the current probing depth.""" return SCIPgetProbingDepth(self._scip) def chgVarObjProbing(self, Variable var, newobj): - """changes (column) variable's objective value during probing mode""" + """Changes (column) variable's objective value during probing mode.""" PY_SCIP_CALL( SCIPchgVarObjProbing(self._scip, var.scip_var, newobj) ) def chgVarLbProbing(self, Variable var, lb): - """changes the variable lower bound during probing mode + """ + Changes the variable lower bound during probing mode. + + Parameters + ---------- + var : Variable + variable to change bound of + lb : float or None + new lower bound (set to None for -infinity) - :param Variable var: variable to change bound of - :param lb: new lower bound (set to None for -infinity) """ if lb is None: lb = -SCIPinfinity(self._scip) PY_SCIP_CALL(SCIPchgVarLbProbing(self._scip, var.scip_var, lb)) def chgVarUbProbing(self, Variable var, ub): - """changes the variable upper bound during probing mode + """ + Changes the variable upper bound during probing mode. + + Parameters + ---------- + var : Variable + variable to change bound of + ub : float or None + new upper bound (set to None for +infinity) - :param Variable var: variable to change bound of - :param ub: new upper bound (set to None for +infinity) """ if ub is None: ub = SCIPinfinity(self._scip) PY_SCIP_CALL(SCIPchgVarUbProbing(self._scip, var.scip_var, ub)) def fixVarProbing(self, Variable var, fixedval): - """Fixes a variable at the current probing node.""" + """ + Fixes a variable at the current probing node. + + Parameters + ---------- + var : Variable + fixedval : float + + """ PY_SCIP_CALL( SCIPfixVarProbing(self._scip, var.scip_var, fixedval) ) def isObjChangedProbing(self): - """returns whether the objective function has changed during probing mode""" + """ + Returns whether the objective function has changed during probing mode. + + Returns + ------- + bool + + """ return SCIPisObjChangedProbing(self._scip) def inProbing(self): - """returns whether we are in probing mode; probing mode is activated via startProbing() and stopped via endProbing()""" + """ + Returns whether we are in probing mode; + probing mode is activated via startProbing() and stopped via endProbing(). + + Returns + ------- + bool + + """ return SCIPinProbing(self._scip) def solveProbingLP(self, itlim = -1): - """solves the LP at the current probing node (cannot be applied at preprocessing stage) - no separation or pricing is applied - :param itlim: maximal number of LP iterations to perform (Default value = -1, that is, no limit) - returns two booleans: - lperror -- if an unresolved lp error occured - cutoff -- whether the LP was infeasible or the objective limit was reached + """ + Solves the LP at the current probing node (cannot be applied at preprocessing stage) + no separation or pricing is applied. + + Parameters + ---------- + itlim : int + maximal number of LP iterations to perform (Default value = -1, that is, no limit) + + Returns + ------- + lperror : bool + if an unresolved lp error occured + cutoff : bool + whether the LP was infeasible or the objective limit was reached + """ cdef SCIP_Bool lperror cdef SCIP_Bool cutoff @@ -4594,11 +7563,17 @@ cdef class Model: return lperror, cutoff def applyCutsProbing(self): - """applies the cuts in the separation storage to the LP and clears the storage afterwards; + """ + Applies the cuts in the separation storage to the LP and clears the storage afterwards; this method can only be applied during probing; the user should resolve the probing LP afterwards - in order to get a new solution + in order to get a new solution. returns: - cutoff -- whether an empty domain was created + + Returns + ------- + cutoff : bool + whether an empty domain was created + """ cdef SCIP_Bool cutoff @@ -4606,14 +7581,24 @@ cdef class Model: return cutoff def propagateProbing(self, maxproprounds): - """applies domain propagation on the probing sub problem, that was changed after SCIPstartProbing() was called; + """ + Applies domain propagation on the probing sub problem, that was changed after SCIPstartProbing() was called; the propagated domains of the variables can be accessed with the usual bound accessing calls SCIPvarGetLbLocal() and SCIPvarGetUbLocal(); the propagation is only valid locally, i.e. the local bounds as well as the changed - bounds due to SCIPchgVarLbProbing(), SCIPchgVarUbProbing(), and SCIPfixVarProbing() are used for propagation - :param maxproprounds: maximal number of propagation rounds (Default value = -1, that is, no limit) - returns: - cutoff -- whether the probing node can be cutoff - ndomredsfound -- number of domain reductions found + bounds due to SCIPchgVarLbProbing(), SCIPchgVarUbProbing(), and SCIPfixVarProbing() are used for propagation. + + Parameters + ---------- + maxproprounds : int + maximal number of propagation rounds (Default value = -1, that is, no limit) + + Returns + ------- + cutoff : bool + whether the probing node can be cutoff + ndomredsfound : int + number of domain reductions found + """ cdef SCIP_Bool cutoff cdef SCIP_Longint ndomredsfound @@ -4632,8 +7617,14 @@ cdef class Model: # Solution functions def writeLP(self, filename="LP.lp"): - """writes current LP to a file - :param filename: file name (Default value = "LP.lp") + """ + Writes current LP to a file. + + Parameters + ---------- + filename : str, optional + file name (Default value = "LP.lp") + """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) locale.setlocale(locale.LC_NUMERIC, "C") @@ -4644,10 +7635,19 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def createSol(self, Heur heur = None, initlp=False): - """Create a new primal solution in the transformed space. + """ + Create a new primal solution in the transformed space. + + Parameters + ---------- + heur : Heur or None, optional + heuristic that found the solution (Default value = None) + initlp : bool, optional + Should the created solution be initialised to the current LP solution instead of all zeros - :param Heur heur: heuristic that found the solution (Default value = None) - :param initlp: Should the created solution be initialised to the current LP solution instead of all zeros + Returns + ------- + Solution """ cdef SCIP_HEUR* _heur @@ -4666,8 +7666,17 @@ cdef class Model: return solution def createPartialSol(self, Heur heur = None): - """Create a partial primal solution, initialized to unknown values. - :param Heur heur: heuristic that found the solution (Default value = None) + """ + Create a partial primal solution, initialized to unknown values. + + Parameters + ---------- + heur : Heur or None, optional + heuristic that found the solution (Default value = None) + + Returns + ------- + Solution """ cdef SCIP_HEUR* _heur @@ -4683,9 +7692,17 @@ cdef class Model: return partialsolution def createOrigSol(self, Heur heur = None): - """Create a new primal solution in the original space. + """ + Create a new primal solution in the original space. + + Parameters + ---------- + heur : Heur or None, optional + heuristic that found the solution (Default value = None) - :param Heur heur: heuristic that found the solution (Default value = None) + Returns + ------- + Solution """ cdef SCIP_HEUR* _heur @@ -4702,7 +7719,15 @@ cdef class Model: return solution def printBestSol(self, write_zeros=False): - """Prints the best feasible primal solution.""" + """ + Prints the best feasible primal solution. + + Parameters + ---------- + write_zeros : bool, optional + include variables that are set to zero (Default = False) + + """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) locale.setlocale(locale.LC_NUMERIC, "C") @@ -4711,11 +7736,16 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def printSol(self, Solution solution=None, write_zeros=False): - """Print the given primal solution. + """ + Print the given primal solution. + + Parameters + ---------- + solution : Solution or None, optional + solution to print (default = None) + write_zeros : bool, optional + include variables that are set to zero (Default=False) - Keyword arguments: - solution -- solution to print - write_zeros -- include variables that are set to zero """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) @@ -4729,11 +7759,16 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def writeBestSol(self, filename="origprob.sol", write_zeros=False): - """Write the best feasible primal solution to a file. + """ + Write the best feasible primal solution to a file. + + Parameters + ---------- + filename : str, optional + name of the output file (Default="origprob.sol") + write_zeros : bool, optional + include variables that are set to zero (Default=False) - Keyword arguments: - filename -- name of the output file - write_zeros -- include variables that are set to zero """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) @@ -4748,11 +7783,16 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def writeBestTransSol(self, filename="transprob.sol", write_zeros=False): - """Write the best feasible primal solution for the transformed problem to a file. + """ + Write the best feasible primal solution for the transformed problem to a file. + + Parameters + ---------- + filename : str, optional + name of the output file (Default="transprob.sol") + write_zeros : bool, optional + include variables that are set to zero (Default=False) - Keyword arguments: - filename -- name of the output file - write_zeros -- include variables that are set to zero """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) locale.setlocale(locale.LC_NUMERIC, "C") @@ -4766,12 +7806,18 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def writeSol(self, Solution solution, filename="origprob.sol", write_zeros=False): - """Write the given primal solution to a file. + """ + Write the given primal solution to a file. + + Parameters + ---------- + solution : Solution + solution to write + filename : str, optional + name of the output file (Default="origprob.sol") + write_zeros : bool, optional + include variables that are set to zero (Default=False) - Keyword arguments: - solution -- solution to write - filename -- name of the output file - write_zeros -- include variables that are set to zero """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) locale.setlocale(locale.LC_NUMERIC, "C") @@ -4785,12 +7831,18 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def writeTransSol(self, Solution solution, filename="transprob.sol", write_zeros=False): - """Write the given transformed primal solution to a file. + """ + Write the given transformed primal solution to a file. + + Parameters + ---------- + solution : Solution + transformed solution to write + filename : str, optional + name of the output file (Default="transprob.sol") + write_zeros : bool, optional + include variables that are set to zero (Default=False) - Keyword arguments: - solution -- transformed solution to write - filename -- name of the output file - write_zeros -- include variables that are set to zero """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) locale.setlocale(locale.LC_NUMERIC, "C") @@ -4806,10 +7858,14 @@ cdef class Model: # perhaps this should not be included as it implements duplicated functionality # (as does it's namesake in SCIP) def readSol(self, filename): - """Reads a given solution file, problem has to be transformed in advance. + """ + Reads a given solution file, problem has to be transformed in advance. + + Parameters + ---------- + filename : str + name of the input file - Keyword arguments: - filename -- name of the input file """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) locale.setlocale(locale.LC_NUMERIC, "C") @@ -4820,13 +7876,21 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC, user_locale) def readSolFile(self, filename): - """Reads a given solution file. + """ + Reads a given solution file. Solution is created but not added to storage/the model. Use 'addSol' OR 'trySol' to add it. - Keyword arguments: - filename -- name of the input file + Parameters + ---------- + filename : str + name of the input file + + Returns + ------- + Solution + """ cdef SCIP_Bool partial cdef SCIP_Bool error @@ -4850,11 +7914,17 @@ cdef class Model: return solution def setSolVal(self, Solution solution, Variable var, val): - """Set a variable in a solution. + """ + Set a variable in a solution. - :param Solution solution: solution to be modified - :param Variable var: variable in the solution - :param val: value of the specified variable + Parameters + ---------- + solution : Solution + solution to be modified + var : Variable + variable in the solution + val : float + value of the specified variable """ cdef SCIP_SOL* _sol @@ -4864,15 +7934,30 @@ cdef class Model: PY_SCIP_CALL(SCIPsetSolVal(self._scip, _sol, var.scip_var, val)) def trySol(self, Solution solution, printreason=True, completely=False, checkbounds=True, checkintegrality=True, checklprows=True, free=True): - """Check given primal solution for feasibility and try to add it to the storage. + """ + Check given primal solution for feasibility and try to add it to the storage. - :param Solution solution: solution to store - :param printreason: should all reasons of violations be printed? (Default value = True) - :param completely: should all violation be checked? (Default value = False) - :param checkbounds: should the bounds of the variables be checked? (Default value = True) - :param checkintegrality: has integrality to be checked? (Default value = True) - :param checklprows: have current LP rows (both local and global) to be checked? (Default value = True) - :param free: should solution be freed? (Default value = True) + Parameters + ---------- + solution : Solution + solution to store + printreason : bool, optional + should all reasons of violations be printed? (Default value = True) + completely : bool, optional + should all violation be checked? (Default value = False) + checkbounds : bool, optional + should the bounds of the variables be checked? (Default value = True) + checkintegrality : bool, optional + does integrality have to be checked? (Default value = True) + checklprows : bool, optional + do current LP rows (both local and global) have to be checked? (Default value = True) + free : bool, optional + should solution be freed? (Default value = True) + + Returns + ------- + stored : bool + whether given solution was feasible and good enough to keep """ cdef SCIP_Bool stored @@ -4883,15 +7968,30 @@ cdef class Model: return stored def checkSol(self, Solution solution, printreason=True, completely=False, checkbounds=True, checkintegrality=True, checklprows=True, original=False): - """Check given primal solution for feasibility without adding it to the storage. + """ + Check given primal solution for feasibility without adding it to the storage. - :param Solution solution: solution to store - :param printreason: should all reasons of violations be printed? (Default value = True) - :param completely: should all violation be checked? (Default value = False) - :param checkbounds: should the bounds of the variables be checked? (Default value = True) - :param checkintegrality: has integrality to be checked? (Default value = True) - :param checklprows: have current LP rows (both local and global) to be checked? (Default value = True) - :param original: must the solution be checked against the original problem (Default value = False) + Parameters + ---------- + solution : Solution + solution to store + printreason : bool, optional + should all reasons of violations be printed? (Default value = True) + completely : bool, optional + should all violation be checked? (Default value = False) + checkbounds : bool, optional + should the bounds of the variables be checked? (Default value = True) + checkintegrality : bool, optional + has integrality to be checked? (Default value = True) + checklprows : bool, optional + have current LP rows (both local and global) to be checked? (Default value = True) + original : bool, optional + must the solution be checked against the original problem (Default value = False) + + Returns + ------- + feasible : bool + whether the given solution was feasible or not """ cdef SCIP_Bool feasible @@ -4902,10 +8002,20 @@ cdef class Model: return feasible def addSol(self, Solution solution, free=True): - """Try to add a solution to the storage. + """ + Try to add a solution to the storage. + + Parameters + ---------- + solution : Solution + solution to store + free : bool, optional + should solution be freed afterwards? (Default value = True) - :param Solution solution: solution to store - :param free: should solution be freed afterwards? (Default value = True) + Returns + ------- + stored : bool + stores whether given solution was good enough to keep """ cdef SCIP_Bool stored @@ -4916,34 +8026,73 @@ cdef class Model: return stored def freeSol(self, Solution solution): - """Free given solution + """ + Free given solution - :param Solution solution: solution to be freed + Parameters + ---------- + solution : Solution + solution to be freed """ PY_SCIP_CALL(SCIPfreeSol(self._scip, &solution.sol)) def getNSols(self): - """gets number of feasible primal solutions stored in the solution storage in case the problem is transformed; - in case the problem stage is SCIP_STAGE_PROBLEM, the number of solution in the original solution candidate - storage is returned - """ + """ + Gets number of feasible primal solutions stored in the solution storage in case the problem is transformed; + in case the problem stage is SCIP_STAGE_PROBLEM, the number of solution in the original solution candidate + storage is returned. + + Returns + ------- + int + + """ return SCIPgetNSols(self._scip) def getNSolsFound(self): - """gets number of feasible primal solutions found so far""" + """ + Gets number of feasible primal solutions found so far. + + Returns + ------- + int + + """ return SCIPgetNSolsFound(self._scip) def getNLimSolsFound(self): - """gets number of feasible primal solutions respecting the objective limit found so far""" + """ + Gets number of feasible primal solutions respecting the objective limit found so far. + + Returns + ------- + int + + """ return SCIPgetNLimSolsFound(self._scip) def getNBestSolsFound(self): - """gets number of feasible primal solutions found so far, that improved the primal bound at the time they were found""" + """ + Gets number of feasible primal solutions found so far, + that improved the primal bound at the time they were found. + + Returns + ------- + int + + """ return SCIPgetNBestSolsFound(self._scip) def getSols(self): - """Retrieve list of all feasible primal solutions stored in the solution storage.""" + """ + Retrieve list of all feasible primal solutions stored in the solution storage. + + Returns + ------- + list of Solution + + """ cdef SCIP_SOL** _sols cdef SCIP_SOL* _sol _sols = SCIPgetSols(self._scip) @@ -4956,15 +8105,30 @@ cdef class Model: return sols def getBestSol(self): - """Retrieve currently best known feasible primal solution.""" + """ + Retrieve currently best known feasible primal solution. + + Returns + ------- + Solution or None + + """ self._bestSol = Solution.create(self._scip, SCIPgetBestSol(self._scip)) return self._bestSol def getSolObjVal(self, Solution sol, original=True): - """Retrieve the objective value of the solution. + """ + Retrieve the objective value of the solution. + + Parameters + ---------- + sol : Solution + original : bool, optional + objective value in original space (Default value = True) - :param Solution sol: solution - :param original: objective value in original space (Default value = True) + Returns + ------- + float """ if sol == None: @@ -4980,17 +8144,33 @@ cdef class Model: return objval def getSolTime(self, Solution sol): - """Get clock time, when this solution was found. + """ + Get clock time when this solution was found. + + Parameters + ---------- + sol : Solution + + Returns + ------- + float - :param Solution sol: solution - """ return SCIPgetSolTime(self._scip, sol.sol) def getObjVal(self, original=True): - """Retrieve the objective value of value of best solution. + """ + Retrieve the objective value of the best solution. + + Parameters + ---------- + original : bool, optional + objective value in original space (Default value = True) + + Returns + ------- + float - :param original: objective value in original space (Default value = True) """ if SCIPgetNSols(self._scip) == 0: @@ -5010,13 +8190,24 @@ cdef class Model: return self.getSolObjVal(self._bestSol, original) def getSolVal(self, Solution sol, Expr expr): - """Retrieve value of given variable or expression in the given solution or in + """ + Retrieve value of given variable or expression in the given solution or in the LP/pseudo solution if sol == None - :param Solution sol: solution - :param Expr expr: polynomial expression to query the value of + Parameters + ---------- + sol : Solution + expr : Expr + polynomial expression to query the value of + + Returns + ------- + float + + Notes + ----- + A variable is also an expression. - Note: a variable is also an expression """ # no need to create a NULL solution wrapper in case we have a variable if sol == None and isinstance(expr, Variable): @@ -5027,12 +8218,23 @@ cdef class Model: return sol[expr] def getVal(self, Expr expr): - """Retrieve the value of the given variable or expression in the best known solution. + """ + Retrieve the value of the given variable or expression in the best known solution. Can only be called after solving is completed. - :param Expr expr: polynomial expression to query the value of + Parameters + ---------- + expr : Expr + polynomial expression to query the value of + + Returns + ------- + float + + Notes + ----- + A variable is also an expression. - Note: a variable is also an expression """ stage_check = SCIPgetStage(self._scip) not in [SCIP_STAGE_INIT, SCIP_STAGE_FREE] @@ -5043,13 +8245,27 @@ cdef class Model: def hasPrimalRay(self): """ - Returns whether a primal ray is stored that proves unboundedness of the LP relaxation + Returns whether a primal ray is stored that proves unboundedness of the LP relaxation. + + Returns + ------- + bool + """ return SCIPhasPrimalRay(self._scip) def getPrimalRayVal(self, Variable var): """ - Gets value of given variable in primal ray causing unboundedness of the LP relaxation + Gets value of given variable in primal ray causing unboundedness of the LP relaxation. + + Parameters + ---------- + var : Variable + + Returns + ------- + float + """ assert SCIPhasPrimalRay(self._scip), "The problem does not have a primal ray." @@ -5057,7 +8273,12 @@ cdef class Model: def getPrimalRay(self): """ - Gets primal ray causing unboundedness of the LP relaxation + Gets primal ray causing unboundedness of the LP relaxation. + + Returns + ------- + list of float + """ assert SCIPhasPrimalRay(self._scip), "The problem does not have a primal ray." @@ -5071,21 +8292,45 @@ cdef class Model: return ray def getPrimalbound(self): - """Retrieve the best primal bound.""" + """ + Retrieve the best primal bound. + + Returns + ------- + float + + """ return SCIPgetPrimalbound(self._scip) def getDualbound(self): - """Retrieve the best dual bound.""" + """ + Retrieve the best dual bound. + + Returns + ------- + float + + """ return SCIPgetDualbound(self._scip) def getDualboundRoot(self): - """Retrieve the best root dual bound.""" + """ + Retrieve the best root dual bound. + + Returns + ------- + float + + """ return SCIPgetDualboundRoot(self._scip) def writeName(self, Variable var): - """Write the name of the variable to the std out. + """ + Write the name of the variable to the std out. - :param Variable var: variable + Parameters + ---------- + var : Variable """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) @@ -5096,24 +8341,46 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def getStage(self): - """Retrieve current SCIP stage""" + """ + Retrieve current SCIP stage. + + Returns + ------- + int + + """ return SCIPgetStage(self._scip) def getStageName(self): - """Returns name of current stage as string""" + """ + Returns name of current stage as string. + + Returns + ------- + str + + """ if not StageNames: self._getStageNames() return StageNames[self.getStage()] def _getStageNames(self): - """Gets names of stages""" + """Gets names of stages.""" for name in dir(PY_SCIP_STAGE): attr = getattr(PY_SCIP_STAGE, name) if isinstance(attr, int): StageNames[attr] = name def getStatus(self): - """Retrieve solution status.""" + """ + Retrieve solution status. + + Returns + ------- + str + The status of SCIP. + + """ cdef SCIP_STATUS stat = SCIPgetStatus(self._scip) if stat == SCIP_STATUS_OPTIMAL: return "optimal" @@ -5151,7 +8418,14 @@ cdef class Model: return "unknown" def getObjectiveSense(self): - """Retrieve objective sense.""" + """ + Retrieve objective sense. + + Returns + ------- + str + + """ cdef SCIP_OBJSENSE sense = SCIPgetObjsense(self._scip) if sense == SCIP_OBJSENSE_MAXIMIZE: return "maximize" @@ -5161,7 +8435,15 @@ cdef class Model: return "unknown" def catchEvent(self, eventtype, Eventhdlr eventhdlr): - """catches a global (not variable or row dependent) event""" + """ + Catches a global (not variable or row dependent) event. + + Parameters + ---------- + eventtype : PY_SCIP_EVENTTYPE + eventhdlr : Eventhdlr + + """ cdef SCIP_EVENTHDLR* _eventhdlr if isinstance(eventhdlr, Eventhdlr): n = str_conversion(eventhdlr.name) @@ -5173,7 +8455,15 @@ cdef class Model: PY_SCIP_CALL(SCIPcatchEvent(self._scip, eventtype, _eventhdlr, NULL, NULL)) def dropEvent(self, eventtype, Eventhdlr eventhdlr): - """drops a global event (stops to track event)""" + """ + Drops a global event (stops tracking the event). + + Parameters + ---------- + eventtype : PY_SCIP_EVENTTYPE + eventhdlr : Eventhdlr + + """ cdef SCIP_EVENTHDLR* _eventhdlr if isinstance(eventhdlr, Eventhdlr): n = str_conversion(eventhdlr.name) @@ -5185,7 +8475,16 @@ cdef class Model: PY_SCIP_CALL(SCIPdropEvent(self._scip, eventtype, _eventhdlr, NULL, -1)) def catchVarEvent(self, Variable var, eventtype, Eventhdlr eventhdlr): - """catches an objective value or domain change event on the given transformed variable""" + """ + Catches an objective value or domain change event on the given transformed variable. + + Parameters + ---------- + var : Variable + eventtype : PY_SCIP_EVENTTYPE + eventhdlr : Eventhdlr + + """ cdef SCIP_EVENTHDLR* _eventhdlr if isinstance(eventhdlr, Eventhdlr): n = str_conversion(eventhdlr.name) @@ -5195,7 +8494,16 @@ cdef class Model: PY_SCIP_CALL(SCIPcatchVarEvent(self._scip, var.scip_var, eventtype, _eventhdlr, NULL, NULL)) def dropVarEvent(self, Variable var, eventtype, Eventhdlr eventhdlr): - """drops an objective value or domain change event (stops to track event) on the given transformed variable""" + """ + Drops an objective value or domain change event (stops tracking the event) on the given transformed variable. + + Parameters + ---------- + var : Variable + eventtype : PY_SCIP_EVENTTYPE + eventhdlr : Eventhdlr + + """ cdef SCIP_EVENTHDLR* _eventhdlr if isinstance(eventhdlr, Eventhdlr): n = str_conversion(eventhdlr.name) @@ -5205,7 +8513,16 @@ cdef class Model: PY_SCIP_CALL(SCIPdropVarEvent(self._scip, var.scip_var, eventtype, _eventhdlr, NULL, -1)) def catchRowEvent(self, Row row, eventtype, Eventhdlr eventhdlr): - """catches a row coefficient, constant, or side change event on the given row""" + """ + Catches a row coefficient, constant, or side change event on the given row. + + Parameters + ---------- + row : Row + eventtype : PY_SCIP_EVENTTYPE + eventhdlr : Eventhdlr + + """ cdef SCIP_EVENTHDLR* _eventhdlr if isinstance(eventhdlr, Eventhdlr): n = str_conversion(eventhdlr.name) @@ -5215,7 +8532,16 @@ cdef class Model: PY_SCIP_CALL(SCIPcatchRowEvent(self._scip, row.scip_row, eventtype, _eventhdlr, NULL, NULL)) def dropRowEvent(self, Row row, eventtype, Eventhdlr eventhdlr): - """drops a row coefficient, constant, or side change event (stops to track event) on the given row""" + """ + Drops a row coefficient, constant, or side change event (stops tracking the event) on the given row. + + Parameters + ---------- + row : Row + eventtype : PY_SCIP_EVENTTYPE + eventhdlr : Eventhdlr + + """ cdef SCIP_EVENTHDLR* _eventhdlr if isinstance(eventhdlr, Eventhdlr): n = str_conversion(eventhdlr.name) @@ -5236,10 +8562,14 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def writeStatistics(self, filename="origprob.stats"): - """Write statistics to a file. + """ + Write statistics to a file. + + Parameters + ---------- + filename : str, optional + name of the output file (Default = "origprob.stats") - Keyword arguments: - filename -- name of the output file """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) locale.setlocale(locale.LC_NUMERIC, "C") @@ -5253,15 +8583,26 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def getNLPs(self): - """gets total number of LPs solved so far""" + """ + Gets total number of LPs solved so far. + + Returns + ------- + int + + """ return SCIPgetNLPs(self._scip) # Verbosity Methods def hideOutput(self, quiet = True): - """Hide the output. + """ + Hide the output. - :param quiet: hide output? (Default value = True) + Parameters + ---------- + quiet : bool, optional + hide output? (Default value = True) """ SCIPsetMessagehdlrQuiet(self._scip, quiet) @@ -5278,8 +8619,14 @@ cdef class Model: SCIPmessageSetErrorPrinting(relayErrorMessage, NULL) def setLogfile(self, path): - """sets the log file name for the currently installed message handler - :param path: name of log file, or None (no log) + """ + Sets the log file name for the currently installed message handler. + + Parameters + ---------- + path : str or None + name of log file, or None (no log) + """ if path: c_path = str_conversion(path) @@ -5290,60 +8637,90 @@ cdef class Model: # Parameter Methods def setBoolParam(self, name, value): - """Set a boolean-valued parameter. + """ + Set a boolean-valued parameter. - :param name: name of parameter - :param value: value of parameter + Parameters + ---------- + name : str + name of parameter + value : bool + value of parameter """ n = str_conversion(name) PY_SCIP_CALL(SCIPsetBoolParam(self._scip, n, value)) def setIntParam(self, name, value): - """Set an int-valued parameter. + """ + Set an int-valued parameter. - :param name: name of parameter - :param value: value of parameter + Parameters + ---------- + name : str + name of parameter + value : int + value of parameter """ n = str_conversion(name) PY_SCIP_CALL(SCIPsetIntParam(self._scip, n, value)) def setLongintParam(self, name, value): - """Set a long-valued parameter. + """ + Set a long-valued parameter. - :param name: name of parameter - :param value: value of parameter + Parameters + ---------- + name : str + name of parameter + value : int + value of parameter """ n = str_conversion(name) PY_SCIP_CALL(SCIPsetLongintParam(self._scip, n, value)) def setRealParam(self, name, value): - """Set a real-valued parameter. + """ + Set a real-valued parameter. - :param name: name of parameter - :param value: value of parameter + Parameters + ---------- + name : str + name of parameter + value : float + value of parameter """ n = str_conversion(name) PY_SCIP_CALL(SCIPsetRealParam(self._scip, n, value)) def setCharParam(self, name, value): - """Set a char-valued parameter. + """ + Set a char-valued parameter. - :param name: name of parameter - :param value: value of parameter + Parameters + ---------- + name : str + name of parameter + value : str + value of parameter """ n = str_conversion(name) PY_SCIP_CALL(SCIPsetCharParam(self._scip, n, ord(value))) def setStringParam(self, name, value): - """Set a string-valued parameter. + """ + Set a string-valued parameter. - :param name: name of parameter - :param value: value of parameter + Parameters + ---------- + name : str + name of parameter + value : str + value of parameter """ n = str_conversion(name) @@ -5353,8 +8730,13 @@ cdef class Model: def setParam(self, name, value): """Set a parameter with value in int, bool, real, long, char or str. - :param name: name of parameter - :param value: value of parameter + Parameters + ---------- + name : str + name of parameter + value : object + value of parameter + """ cdef SCIP_PARAM* param @@ -5382,10 +8764,19 @@ cdef class Model: def getParam(self, name): - """Get the value of a parameter of type + """ + Get the value of a parameter of type int, bool, real, long, char or str. - :param name: name of parameter + Parameters + ---------- + name : str + name of parameter + + Returns + ------- + object + """ cdef SCIP_PARAM* param @@ -5411,8 +8802,16 @@ cdef class Model: return SCIPparamGetString(param).decode('utf-8') def getParams(self): - """Gets the values of all parameters as a dict mapping parameter names - to their values.""" + """ + Gets the values of all parameters as a dict mapping parameter names + to their values. + + Returns + ------- + dict of str to object + dict mapping parameter names to their values. + + """ cdef SCIP_PARAM** params params = SCIPgetParams(self._scip) @@ -5423,17 +8822,26 @@ cdef class Model: return result def setParams(self, params): - """Sets multiple parameters at once. + """ + Sets multiple parameters at once. + + Parameters + ---------- + params : dict of str to object + dict mapping parameter names to their values. - :param params: dict mapping parameter names to their values. """ for name, value in params.items(): self.setParam(name, value) def readParams(self, file): - """Read an external parameter file. + """ + Read an external parameter file. - :param file: file to be read + Parameters + ---------- + file : str + file to read """ absfile = str_conversion(abspath(file)) @@ -5446,12 +8854,20 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC, user_locale) def writeParams(self, filename='param.set', comments=True, onlychanged=True, verbose=True): - """Write parameter settings to an external file. + """ + Write parameter settings to an external file. + + Parameters + ---------- + filename : str, optional + file to be written (Default value = 'param.set') + comments : bool, optional + write parameter descriptions as comments? (Default value = True) + onlychanged : bool, optional + write only modified parameters (Default value = True) + verbose : bool, optional + indicates whether a success message should be printed - :param filename: file to be written (Default value = 'param.set') - :param comments: write parameter descriptions as comments? (Default value = True) - :param onlychanged: write only modified parameters (Default value = True) - :param verbose: indicates whether a success message should be printed """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) locale.setlocale(locale.LC_NUMERIC, "C") @@ -5466,32 +8882,46 @@ cdef class Model: locale.setlocale(locale.LC_NUMERIC,user_locale) def resetParam(self, name): - """Reset parameter setting to its default value + """ + Reset parameter setting to its default value - :param name: parameter to reset + Parameters + ---------- + name : str + parameter to reset """ n = str_conversion(name) PY_SCIP_CALL(SCIPresetParam(self._scip, n)) def resetParams(self): - """Reset parameter settings to their default values""" + """Reset parameter settings to their default values.""" PY_SCIP_CALL(SCIPresetParams(self._scip)) def setEmphasis(self, paraemphasis, quiet = True): - """Set emphasis settings + """ + Set emphasis settings - :param paraemphasis: emphasis to set - :param quiet: hide output? (Default value = True) + Parameters + ---------- + paraemphasis : PY_SCIP_PARAMEMPHASIS + emphasis to set + quiet : bool, optional + hide output? (Default value = True) """ PY_SCIP_CALL(SCIPsetEmphasis(self._scip, paraemphasis, quiet)) def readProblem(self, filename, extension = None): - """Read a problem instance from an external file. + """ + Read a problem instance from an external file. - :param filename: problem file name - :param extension: specify file extension/type (Default value = None) + Parameters + ---------- + filename : str + problem file name + extension : str or None + specify file extension/type (Default value = None) """ user_locale = locale.getlocale(category=locale.LC_NUMERIC) @@ -5513,11 +8943,25 @@ cdef class Model: PY_SCIP_CALL(SCIPcount(self._scip)) def getNReaders(self): - """Get number of currently available readers.""" + """ + Get number of currently available readers. + + Returns + ------- + int + + """ return SCIPgetNReaders(self._scip) def getNCountedSols(self): - """Get number of feasible solution.""" + """ + Get number of feasible solution. + + Returns + ------- + int + + """ cdef SCIP_Bool valid cdef SCIP_Longint nsols @@ -5531,14 +8975,19 @@ cdef class Model: PY_SCIP_CALL(SCIPsetParamsCountsols(self._scip)) def freeReoptSolve(self): - """Frees all solution process data and prepares for reoptimization""" + """Frees all solution process data and prepares for reoptimization.""" PY_SCIP_CALL(SCIPfreeReoptSolve(self._scip)) def chgReoptObjective(self, coeffs, sense = 'minimize'): - """Establish the objective function as a linear expression. + """ + Establish the objective function as a linear expression. - :param coeffs: the coefficients - :param sense: the objective sense (Default value = 'minimize') + Parameters + ---------- + coeffs : list of float + the coefficients + sense : str + the objective sense (Default value = 'minimize') """ @@ -5581,11 +9030,18 @@ cdef class Model: free(_coeffs) def chgVarBranchPriority(self, Variable var, priority): - """Sets the branch priority of the variable. - Variables with higher branch priority are always preferred to variables with lower priority in selection of branching variable. + """ + Sets the branch priority of the variable. + Variables with higher branch priority are always preferred to variables with + lower priority in selection of branching variable. + + Parameters + ---------- + var : Variable + variable to change priority of + priority : int + the new priority of the variable (the default branching priority is 0) - :param Variable var: variable to change priority of - :param priority: the new priority of the variable (the default branching priority is 0) """ assert isinstance(var, Variable), "The given variable is not a pyvar, but %s" % var.__class__.__name__ PY_SCIP_CALL(SCIPchgVarBranchPriority(self._scip, var.scip_var, priority)) @@ -5593,28 +9049,42 @@ cdef class Model: def startStrongbranch(self): """Start strong branching. Needs to be called before any strong branching. Must also later end strong branching. TODO: Propagation option has currently been disabled via Python. - If propagation is enabled then strong branching is not done on the LP, but on additionally created nodes (has some overhead)""" + If propagation is enabled then strong branching is not done on the LP, but on additionally created nodes + (has some overhead). """ PY_SCIP_CALL(SCIPstartStrongbranch(self._scip, False)) def endStrongbranch(self): """End strong branching. Needs to be called if startStrongBranching was called previously. - Between these calls the user can access all strong branching functionality. """ + Between these calls the user can access all strong branching functionality.""" PY_SCIP_CALL(SCIPendStrongbranch(self._scip)) def getVarStrongbranchLast(self, Variable var): - """Get the results of the last strong branching call on this variable (potentially was called + """ + Get the results of the last strong branching call on this variable (potentially was called at another node). - down - The dual bound of the LP after branching down on the variable - up - The dual bound of the LP after branchign up on the variable - downvalid - Whether down stores a valid dual bound or is NULL - upvalid - Whether up stores a valid dual bound or is NULL - solval - The solution value of the variable at the last strong branching call - lpobjval - The LP objective value at the time of the last strong branching call + Parameters + ---------- + var : Variable + variable to get the previous strong branching information from + + Returns + ------- + down : float + The dual bound of the LP after branching down on the variable + up : float + The dual bound of the LP after branchign up on the variable + downvalid : bool + stores whether the returned down value is a valid dual bound, or NULL + upvalid : bool + stores whether the returned up value is a valid dual bound, or NULL + solval : float + The solution value of the variable at the last strong branching call + lpobjval : float + The LP objective value at the time of the last strong branching call - :param Variable var: variable to get the previous strong branching information from """ cdef SCIP_Real down @@ -5629,9 +9099,18 @@ cdef class Model: return down, up, downvalid, upvalid, solval, lpobjval def getVarStrongbranchNode(self, Variable var): - """Get the node number from the last time strong branching was called on the variable + """ + Get the node number from the last time strong branching was called on the variable. + + Parameters + ---------- + var : Variable + variable to get the previous strong branching node from + + Returns + ------- + int - :param Variable var: variable to get the previous strong branching node from """ cdef SCIP_Longint node_num @@ -5640,12 +9119,41 @@ cdef class Model: return node_num def getVarStrongbranch(self, Variable var, itlim, idempotent=False, integral=False): - """ Strong branches and gets information on column variable. + """ + Strong branches and gets information on column variable. + + Parameters + ---------- + var : Variable + Variable to get strong branching information on + itlim : int + LP iteration limit for total strong branching calls + idempotent : bool, optional + Should SCIP's state remain the same after the call? + integral : bool, optional + Boolean on whether the variable is currently integer. + + Returns + ------- + down : float + The dual bound of the LP after branching down on the variable + up : float + The dual bound of the LP after branchign up on the variable + downvalid : bool + stores whether the returned down value is a valid dual bound, or NULL + upvalid : bool + stores whether the returned up value is a valid dual bound, or NULL + downinf : bool + store whether the downwards branch is infeasible + upinf : bool + store whether the upwards branch is infeasible + downconflict : bool + store whether a conflict constraint was created for an infeasible downwards branch + upconflict : bool + store whether a conflict constraint was created for an infeasible upwards branch + lperror : bool + whether an unresolved LP error occurred in the solving process - :param Variable var: Variable to get strong branching information on - :param itlim: LP iteration limit for total strong branching calls - :param idempotent: Should SCIP's state remain the same after the call? - :param integral: Boolean on whether the variable is currently integer. """ cdef SCIP_Real down @@ -5668,25 +9176,43 @@ cdef class Model: return down, up, downvalid, upvalid, downinf, upinf, downconflict, upconflict, lperror def updateVarPseudocost(self, Variable var, valdelta, objdelta, weight): - """Updates the pseudo costs of the given variable and the global pseudo costs after a change of valdelta - in the variable's solution value and resulting change of objdelta in the LP's objective value. - Update is ignored if objdelts is infinite. Weight is in range (0, 1], and affects how it updates - the global weighted sum. + """ + Updates the pseudo costs of the given variable and the global pseudo costs after a change of valdelta + in the variable's solution value and resulting change of objdelta in the LP's objective value. + Update is ignored if objdelts is infinite. Weight is in range (0, 1], and affects how it updates + the global weighted sum. + + Parameters + ---------- + var : Variable + Variable whos pseudo cost will be updated + valdelta : float + The change in variable value (e.g. the fractional amount removed or added by branching) + objdelta : float + The change in objective value of the LP after valdelta change of the variable + weight : float + the weight in range (0,1] of how the update affects the stored weighted sum. - :param Variable var: Variable whos pseudo cost will be updated - :param valdelta: The change in variable value (e.g. the fractional amount removed or added by branching) - :param objdelta: The change in objective value of the LP after valdelta change of the variable - :param weight: the weight in range (0,1] of how the update affects the stored weighted sum. - """ + """ PY_SCIP_CALL(SCIPupdateVarPseudocost(self._scip, var.scip_var, valdelta, objdelta, weight)) def getBranchScoreMultiple(self, Variable var, gains): - """Calculates the branching score out of the gain predictions for a branching with - arbitrary many children. + """ + Calculates the branching score out of the gain predictions for a branching with + arbitrarily many children. + + Parameters + ---------- + var : Variable + variable to calculate the score for + gains : list of float + list of gains for each child. + + Returns + ------- + float - :param Variable var: variable to calculate the score for - :param gains: list of gains for each child. """ assert isinstance(gains, list) @@ -5704,13 +9230,21 @@ cdef class Model: return score def getTreesizeEstimation(self): - """Get an estimation of the final tree size """ + """ + Get an estimate of the final tree size. + + Returns + ------- + float + + """ return SCIPgetTreesizeEstimation(self._scip) def getBipartiteGraphRepresentation(self, prev_col_features=None, prev_edge_features=None, prev_row_features=None, static_only=False, suppress_warnings=False): - """This function generates the bipartite graph representation of an LP, which was first used in + """ + This function generates the bipartite graph representation of an LP, which was first used in the following paper: @inproceedings{conf/nips/GasseCFCL19, title={Exact Combinatorial Optimization with Graph Convolutional Neural Networks}, @@ -5725,11 +9259,27 @@ cdef class Model: An example plugin is a branching rule or an event handler, which is exclusively created to call this function. The user must then make certain to return the appropriate SCIP_RESULT (e.g. DIDNOTRUN) - :param prev_col_features: The list of column features previously returned by this function - :param prev_edge_features: The list of edge features previously returned by this function - :param prev_row_features: The list of row features previously returned by this function - :param static_only: Whether exclusively static features should be generated - :param suppress_warnings: Whether warnings should be suppressed + Parameters + ---------- + prev_col_features : list of list or None, optional + The list of column features previously returned by this function + prev_edge_features : list of list or None, optional + The list of edge features previously returned by this function + prev_row_features : list of list or None, optional + The list of row features previously returned by this function + static_only : bool, optional + Whether exclusively static features should be generated + suppress_warnings : bool, optional + Whether warnings should be suppressed + + Returns + ------- + col_features : list of list + edge_features : list of list + row_features : list of list + dict + The feature mappings for the columns, edges, and rows + """ cdef SCIP* scip = self._scip @@ -6105,8 +9655,15 @@ def readStatistics(filename): Given a .stats file of a solved model, reads it and returns an instance of the Statistics class holding some statistics. - Keyword arguments: - filename -- name of the input file + Parameters + ---------- + filename : str + name of the input file + + Returns + ------- + Statistics + """ result = {} file = open(filename)